Unverified Commit bde351ee authored by Shi-Hao Hong's avatar Shi-Hao Hong Committed by GitHub

Make AlertDialog scrollable through AlertDialog.scrollable parameter (#45079)

* Add AlertDialog.scrollable

* Add deprecation notice

* Ignore deprecation warning in dialog.dart with TODO
parent 7850e252
...@@ -2,6 +2,11 @@ ...@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// TODO(shihaohong): remove ignoring deprecated member use analysis
// when AlertDialog.scrollable parameter is removed. See
// https://flutter.dev/go/scrollable-alert-dialog for more details.
// ignore_for_file: deprecated_member_use_from_same_package
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
...@@ -220,6 +225,7 @@ class AlertDialog extends StatelessWidget { ...@@ -220,6 +225,7 @@ class AlertDialog extends StatelessWidget {
this.elevation, this.elevation,
this.semanticLabel, this.semanticLabel,
this.shape, this.shape,
this.scrollable = false,
}) : assert(contentPadding != null), }) : assert(contentPadding != null),
super(key: key); super(key: key);
...@@ -306,6 +312,22 @@ class AlertDialog extends StatelessWidget { ...@@ -306,6 +312,22 @@ class AlertDialog extends StatelessWidget {
/// {@macro flutter.material.dialog.shape} /// {@macro flutter.material.dialog.shape}
final ShapeBorder shape; final ShapeBorder shape;
/// Determines whether the [title] and [content] widgets are wrapped in a
/// scrollable.
///
/// This configuration is used when the [title] and [content] are expected
/// to overflow. Both [title] and [content] are wrapped in a scroll view,
/// allowing all overflowed content to be visible while still showing the
/// button bar.
@Deprecated(
'Set scrollable to `true`. This parameter will be removed and '
'was introduced to migrate AlertDialog to be scrollable by '
'default. For more information, see '
'https://flutter.dev/docs/release/breaking-changes/scrollable_alert_dialog. '
'This feature was deprecated after v1.13.2.'
)
final bool scrollable;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
...@@ -325,38 +347,67 @@ class AlertDialog extends StatelessWidget { ...@@ -325,38 +347,67 @@ class AlertDialog extends StatelessWidget {
} }
} }
Widget titleWidget;
Widget contentWidget;
if (title != null)
titleWidget = Padding(
padding: titlePadding ?? EdgeInsets.fromLTRB(24.0, 24.0, 24.0, content == null ? 20.0 : 0.0),
child: DefaultTextStyle(
style: titleTextStyle ?? dialogTheme.titleTextStyle ?? theme.textTheme.title,
child: Semantics(
child: title,
namesRoute: true,
container: true,
),
),
);
if (content != null)
contentWidget = Padding(
padding: contentPadding,
child: DefaultTextStyle(
style: contentTextStyle ?? dialogTheme.contentTextStyle ?? theme.textTheme.subhead,
child: content,
),
);
List<Widget> columnChildren;
if (scrollable) {
columnChildren = <Widget>[
if (title != null || content != null)
Flexible(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
if (title != null)
titleWidget,
if (content != null)
contentWidget,
],
),
),
),
if (actions != null)
ButtonBar(children: actions),
];
} else {
columnChildren = <Widget>[
if (title != null)
titleWidget,
if (content != null)
Flexible(child: contentWidget),
if (actions != null)
ButtonBar(children: actions),
];
}
Widget dialogChild = IntrinsicWidth( Widget dialogChild = IntrinsicWidth(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[ children: columnChildren,
if (title != null)
Padding(
padding: titlePadding ?? EdgeInsets.fromLTRB(24.0, 24.0, 24.0, content == null ? 20.0 : 0.0),
child: DefaultTextStyle(
style: titleTextStyle ?? dialogTheme.titleTextStyle ?? theme.textTheme.title,
child: Semantics(
child: title,
namesRoute: true,
container: true,
),
),
),
if (content != null)
Flexible(
child: Padding(
padding: contentPadding,
child: DefaultTextStyle(
style: contentTextStyle ?? dialogTheme.contentTextStyle ?? theme.textTheme.subhead,
child: content,
),
),
),
if (actions != null)
ButtonBar(
children: actions,
),
],
), ),
); );
......
...@@ -716,6 +716,86 @@ void main() { ...@@ -716,6 +716,86 @@ void main() {
expect(rootObserver.dialogCount, 0); expect(rootObserver.dialogCount, 0);
expect(nestedObserver.dialogCount, 1); expect(nestedObserver.dialogCount, 1);
}); });
group('AlertDialog.scrollable: ', () {
testWidgets('Title is scrollable', (WidgetTester tester) async {
final Key titleKey = UniqueKey();
final AlertDialog dialog = AlertDialog(
title: Container(
key: titleKey,
color: Colors.green,
height: 1000,
),
scrollable: true,
);
await tester.pumpWidget(_appWithAlertDialog(tester, dialog));
await tester.tap(find.text('X'));
await tester.pumpAndSettle();
final RenderBox box = tester.renderObject(find.byKey(titleKey));
final Offset originalOffset = box.localToGlobal(Offset.zero);
await tester.drag(find.byKey(titleKey), const Offset(0.0, -200.0));
expect(box.localToGlobal(Offset.zero), equals(originalOffset.translate(0.0, -200.0)));
});
testWidgets('Content is scrollable', (WidgetTester tester) async {
final Key contentKey = UniqueKey();
final AlertDialog dialog = AlertDialog(
content: Container(
key: contentKey,
color: Colors.orange,
height: 1000,
),
scrollable: true,
);
await tester.pumpWidget(_appWithAlertDialog(tester, dialog));
await tester.tap(find.text('X'));
await tester.pumpAndSettle();
final RenderBox box = tester.renderObject(find.byKey(contentKey));
final Offset originalOffset = box.localToGlobal(Offset.zero);
await tester.drag(find.byKey(contentKey), const Offset(0.0, -200.0));
expect(box.localToGlobal(Offset.zero), equals(originalOffset.translate(0.0, -200.0)));
});
testWidgets('Title and content are scrollable', (WidgetTester tester) async {
final Key titleKey = UniqueKey();
final Key contentKey = UniqueKey();
final AlertDialog dialog = AlertDialog(
title: Container(
key: titleKey,
color: Colors.green,
height: 400,
),
content: Container(
key: contentKey,
color: Colors.orange,
height: 400,
),
scrollable: true,
);
await tester.pumpWidget(_appWithAlertDialog(tester, dialog));
await tester.tap(find.text('X'));
await tester.pumpAndSettle();
final RenderBox title = tester.renderObject(find.byKey(titleKey));
final RenderBox content = tester.renderObject(find.byKey(contentKey));
final Offset titleOriginalOffset = title.localToGlobal(Offset.zero);
final Offset contentOriginalOffset = content.localToGlobal(Offset.zero);
// Dragging the title widget should scroll both the title
// and the content widgets.
await tester.drag(find.byKey(titleKey), const Offset(0.0, -200.0));
expect(title.localToGlobal(Offset.zero), equals(titleOriginalOffset.translate(0.0, -200.0)));
expect(content.localToGlobal(Offset.zero), equals(contentOriginalOffset.translate(0.0, -200.0)));
// Dragging the content widget should scroll both the title
// and the content widgets.
await tester.drag(find.byKey(contentKey), const Offset(0.0, 200.0));
expect(title.localToGlobal(Offset.zero), equals(titleOriginalOffset));
expect(content.localToGlobal(Offset.zero), equals(contentOriginalOffset));
});
});
} }
class DialogObserver extends NavigatorObserver { class DialogObserver extends NavigatorObserver {
......
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