diff --git a/examples/flutter_gallery/lib/demo/dialog_demo.dart b/examples/flutter_gallery/lib/demo/dialog_demo.dart index 7f276c60b5..3548ad9b93 100644 --- a/examples/flutter_gallery/lib/demo/dialog_demo.dart +++ b/examples/flutter_gallery/lib/demo/dialog_demo.dart @@ -29,22 +29,19 @@ class DialogDemoItem extends StatelessWidget { @override Widget build(BuildContext context) { - return new InkWell( - onTap: onPressed, - child: new Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 24.0), - child: new Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - new Icon(icon, size: 36.0, color: color), - new Padding( - padding: const EdgeInsets.only(left: 16.0), - child: new Text(text) - ) - ] - ) - ) + return new SimpleDialogOption( + onPressed: onPressed, + child: new Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + new Icon(icon, size: 36.0, color: color), + new Padding( + padding: const EdgeInsets.only(left: 16.0), + child: new Text(text), + ), + ], + ), ); } } diff --git a/packages/flutter/lib/src/material/debug.dart b/packages/flutter/lib/src/material/debug.dart index 3e53798594..3ed0dab37b 100644 --- a/packages/flutter/lib/src/material/debug.dart +++ b/packages/flutter/lib/src/material/debug.dart @@ -5,7 +5,6 @@ import 'package:flutter/widgets.dart'; import 'material.dart'; -import 'scaffold.dart'; /// Asserts that the given context has a [Material] ancestor. /// @@ -42,37 +41,3 @@ bool debugCheckHasMaterial(BuildContext context) { }); return true; } - -/// Asserts that the given context has a [Scaffold] ancestor. -/// -/// Used by some material design widgets to make sure that they are -/// only used in contexts where they can communicate with a Scaffold. -/// -/// For example, the [AppBar] in some situations requires a Scaffold -/// to do the right thing with scrolling. -/// -/// To call this function, use the following pattern, typically in the -/// relevant Widget's [build] method: -/// -/// ```dart -/// assert(debugCheckHasScaffold(context)); -/// ``` -/// -/// Does nothing if asserts are disabled. Always returns true. -bool debugCheckHasScaffold(BuildContext context) { - assert(() { - if (Scaffold.of(context) == null) { - Element element = context; - throw new FlutterError( - 'No Scaffold widget found.\n' - '${context.widget.runtimeType} widgets require a Scaffold widget ancestor.\n' - 'The specific widget that could not find a Scaffold ancestor was:\n' - ' ${context.widget}\n' - 'The ownership chain for the affected widget is:\n' - ' ${element.debugGetCreatorChain(10)}' - ); - } - return true; - }); - return true; -} diff --git a/packages/flutter/lib/src/material/dialog.dart b/packages/flutter/lib/src/material/dialog.dart index fbfb819857..02d5baae22 100644 --- a/packages/flutter/lib/src/material/dialog.dart +++ b/packages/flutter/lib/src/material/dialog.dart @@ -10,6 +10,7 @@ import 'package:meta/meta.dart'; import 'button.dart'; import 'button_bar.dart'; import 'colors.dart'; +import 'ink_well.dart'; import 'material.dart'; import 'theme.dart'; @@ -176,6 +177,50 @@ class AlertDialog extends StatelessWidget { } } +/// An option used in a [SimpleDialog]. +/// +/// A simple dialog offers the user a choice between several options. This +/// widget is commonly used to represent each of the options. If the user +/// selects this option, the widget will call the [onPressed] callback, which +/// typically uses [Navigator.pop] to close the dialog. +/// +/// See also: +/// +/// * [SimpleDialog], for a dialog in which to use this widget. +/// * [showDialog], which actually displays the dialog and returns its result. +/// * [FlatButton], which are commonly used as actions in other kinds of +/// dialogs, such as [AlertDialog]s. +/// * +class SimpleDialogOption extends StatelessWidget { + /// Creates an option for a [SimpleDialog]. + SimpleDialogOption({ + Key key, + this.onPressed, + this.child, + }) : super(key: key); + + /// The callback that is called when this option is selected. + /// + /// If this is set to null, the option cannot be selected. + final VoidCallback onPressed; + + /// The widget below this widget in the tree. + /// + /// Typically a [Text] widget. + final Widget child; + + @override + Widget build(BuildContext context) { + return new InkWell( + onTap: onPressed, + child: new Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 24.0), + child: child + ), + ); + } +} + /// A simple material design dialog. /// /// A simple dialog offers the user a choice between several options. A simple @@ -189,6 +234,7 @@ class AlertDialog extends StatelessWidget { /// /// See also: /// +/// * [SimpleDialogOption], which are options used in this type of dialog. /// * [AlertDialog], for dialogs that have a row of buttons below the body. /// * [Dialog], on which [SimpleDialog] and [AlertDialog] are based. /// * [showDialog], which actually displays the dialog and returns its result. @@ -220,8 +266,7 @@ class SimpleDialog extends StatelessWidget { /// The (optional) content of the dialog is displayed in a [Block] underneath /// the title. /// - /// The children are assumed to have 8.0 pixels of vertical and 24.0 pixels of - /// horizontal padding internally. + /// Typically a list of [SimpleDialogOption]s. final List children; /// Padding around the content. diff --git a/packages/flutter/lib/src/material/flat_button.dart b/packages/flutter/lib/src/material/flat_button.dart index e0f323d0c3..16a7c69e27 100644 --- a/packages/flutter/lib/src/material/flat_button.dart +++ b/packages/flutter/lib/src/material/flat_button.dart @@ -31,8 +31,10 @@ import 'theme.dart'; /// /// See also: /// -/// * [RaisedButton] -/// * [DropdownButton] +/// * [RaisedButton], which is a button that hovers above the containing +/// material. +/// * [DropdownButton], which offers the user a choice of a number of options. +/// * [SimpleDialogOption], which is used in [SimpleDialog]s. /// * class FlatButton extends StatelessWidget { /// Creates a flat button. diff --git a/packages/flutter/lib/src/material/raised_button.dart b/packages/flutter/lib/src/material/raised_button.dart index 5f8a8c395e..7468216b7c 100644 --- a/packages/flutter/lib/src/material/raised_button.dart +++ b/packages/flutter/lib/src/material/raised_button.dart @@ -114,13 +114,13 @@ class RaisedButton extends StatelessWidget { if (disabledColor != null) return disabledColor; Brightness brightness = Theme.of(context).brightness; + assert(brightness != null); switch (brightness) { case Brightness.light: return Colors.black12; case Brightness.dark: return Colors.white12; } - assert(brightness != null); return null; } } diff --git a/packages/flutter/test/material/arc_test.dart b/packages/flutter/test/material/arc_test.dart index 5112f8ac2a..1a2c308b1c 100644 --- a/packages/flutter/test/material/arc_test.dart +++ b/packages/flutter/test/material/arc_test.dart @@ -6,6 +6,37 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/material.dart'; void main() { + test('MaterialPointArcTween control test', () { + MaterialPointArcTween a = new MaterialPointArcTween( + begin: Point.origin, + end: const Point(0.0, 10.0) + ); + + MaterialPointArcTween b = new MaterialPointArcTween( + begin: Point.origin, + end: const Point(0.0, 10.0) + ); + + expect(a, hasOneLineDescription); + expect(a, equals(b)); + expect(a.hashCode, equals(b.hashCode)); + }); + + test('MaterialRectArcTween control test', () { + MaterialRectArcTween a = new MaterialRectArcTween( + begin: new Rect.fromLTWH(0.0, 0.0, 10.0, 10.0), + end: new Rect.fromLTWH(0.0, 10.0, 10.0, 10.0) + ); + + MaterialRectArcTween b = new MaterialRectArcTween( + begin: new Rect.fromLTWH(0.0, 0.0, 10.0, 10.0), + end: new Rect.fromLTWH(0.0, 10.0, 10.0, 10.0) + ); + expect(a, hasOneLineDescription); + expect(a, equals(b)); + expect(a.hashCode, equals(b.hashCode)); + }); + test('on-axis MaterialPointArcTween', () { MaterialPointArcTween tween = new MaterialPointArcTween( begin: Point.origin, diff --git a/packages/flutter/test/material/debug_test.dart b/packages/flutter/test/material/debug_test.dart new file mode 100644 index 0000000000..06a7fc4f51 --- /dev/null +++ b/packages/flutter/test/material/debug_test.dart @@ -0,0 +1,16 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('debugCheckHasMaterial control test', (WidgetTester tester) async { + await tester.pumpWidget(new FlatButton( + onPressed: null, + child: new Text('Go'), + )); + expect(tester.takeException(), isFlutterError); + }); +} diff --git a/packages/flutter/test/material/dialog_test.dart b/packages/flutter/test/material/dialog_test.dart index f245634786..b48d3f6eea 100644 --- a/packages/flutter/test/material/dialog_test.dart +++ b/packages/flutter/test/material/dialog_test.dart @@ -55,7 +55,7 @@ void main() { await tester.tap(find.text('OK')); expect(didPressOk, true); }); - + testWidgets('Dialog background color', (WidgetTester tester) async { await tester.pumpWidget( @@ -71,18 +71,18 @@ void main() { showDialog( context: context, child: new AlertDialog( + title: new Text('Title'), content: new Text('Y'), - actions: [ - ] - ) + actions: [ ], + ), ); - } - ) + }, + ), ); - } - ) - ) - ) + }, + ), + ), + ), ); await tester.tap(find.text('X')); @@ -96,4 +96,49 @@ void main() { expect(materialconfig.elevation, 24); expect(materialconfig.color, Colors.grey[800]); }); + + testWidgets('Simple dialog control test', (WidgetTester tester) async { + await tester.pumpWidget( + new MaterialApp( + home: new Material( + child: new Center( + child: new RaisedButton( + onPressed: null, + child: new Text('Go'), + ), + ), + ), + ), + ); + + BuildContext context = tester.element(find.text('Go')); + + Future result = showDialog( + context: context, + child: new SimpleDialog( + title: new Text('Title'), + children: [ + new SimpleDialogOption( + onPressed: () { + Navigator.pop(context, 42); + }, + child: new Text('First option'), + ), + new SimpleDialogOption( + child: new Text('Second option'), + ), + ], + ), + ); + + await tester.pumpUntilNoTransientCallbacks(const Duration(seconds: 1)); + expect(find.text('Title'), findsOneWidget); + await tester.tap(find.text('First option')); + + expect(await result, equals(42)); + + // TODO(abarth): Remove once https://github.com/flutter/flutter/issues/7457 + // is fixed. + await tester.pumpUntilNoTransientCallbacks(const Duration(seconds: 1)); + }); } diff --git a/packages/flutter/test/material/ink_well_test.dart b/packages/flutter/test/material/ink_well_test.dart new file mode 100644 index 0000000000..7647bf6ce8 --- /dev/null +++ b/packages/flutter/test/material/ink_well_test.dart @@ -0,0 +1,47 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('InkWell gestures control test', (WidgetTester tester) async { + List log = []; + + await tester.pumpWidget(new Material( + child: new Center( + child: new InkWell( + onTap: () { + log.add('tap'); + }, + onDoubleTap: () { + log.add('double-tap'); + }, + onLongPress: () { + log.add('long-press'); + }, + ), + ), + )); + + await tester.tap(find.byType(InkWell), pointer: 1); + + expect(log, isEmpty); + + await tester.pump(const Duration(seconds: 1)); + + expect(log, equals(['tap'])); + log.clear(); + + await tester.tap(find.byType(InkWell), pointer: 2); + await tester.tap(find.byType(InkWell), pointer: 3); + + expect(log, equals(['double-tap'])); + log.clear(); + + await tester.longPress(find.byType(InkWell), pointer: 4); + + expect(log, equals(['long-press'])); + }); +}