Commit cbfde965 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Appbar should update when you add a drawer. (#9755)

Also, I had a question about flutter_test matchers and our style guide
says that when I have a question I should update the docs so I did
that and then got a bit carried away.
parent d604791a
...@@ -319,20 +319,6 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -319,20 +319,6 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
} }
class _AppBarState extends State<AppBar> { class _AppBarState extends State<AppBar> {
bool _hasDrawer = false;
bool _canPop = false;
bool _useCloseButton = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
final ScaffoldState scaffold = Scaffold.of(context, nullOk: true);
_hasDrawer = scaffold?.hasDrawer ?? false;
final ModalRoute<dynamic> parentRoute = ModalRoute.of(context);
_canPop = parentRoute?.canPop ?? false;
_useCloseButton = parentRoute is MaterialPageRoute<dynamic> && parentRoute.fullscreenDialog;
}
void _handleDrawerButton() { void _handleDrawerButton() {
Scaffold.of(context).openDrawer(); Scaffold.of(context).openDrawer();
} }
...@@ -341,6 +327,12 @@ class _AppBarState extends State<AppBar> { ...@@ -341,6 +327,12 @@ class _AppBarState extends State<AppBar> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(!widget.primary || debugCheckHasMediaQuery(context)); assert(!widget.primary || debugCheckHasMediaQuery(context));
final ThemeData themeData = Theme.of(context); final ThemeData themeData = Theme.of(context);
final ScaffoldState scaffold = Scaffold.of(context, nullOk: true);
final ModalRoute<dynamic> parentRoute = ModalRoute.of(context);
final bool hasDrawer = scaffold?.hasDrawer ?? false;
final bool canPop = parentRoute?.canPop ?? false;
final bool useCloseButton = parentRoute is MaterialPageRoute<dynamic> && parentRoute.fullscreenDialog;
IconThemeData appBarIconTheme = widget.iconTheme ?? themeData.primaryIconTheme; IconThemeData appBarIconTheme = widget.iconTheme ?? themeData.primaryIconTheme;
TextStyle centerStyle = widget.textTheme?.title ?? themeData.primaryTextTheme.title; TextStyle centerStyle = widget.textTheme?.title ?? themeData.primaryTextTheme.title;
...@@ -365,15 +357,15 @@ class _AppBarState extends State<AppBar> { ...@@ -365,15 +357,15 @@ class _AppBarState extends State<AppBar> {
final List<Widget> toolbarChildren = <Widget>[]; final List<Widget> toolbarChildren = <Widget>[];
Widget leading = widget.leading; Widget leading = widget.leading;
if (leading == null) { if (leading == null) {
if (_hasDrawer) { if (hasDrawer) {
leading = new IconButton( leading = new IconButton(
icon: const Icon(Icons.menu), icon: const Icon(Icons.menu),
onPressed: _handleDrawerButton, onPressed: _handleDrawerButton,
tooltip: 'Open navigation menu' // TODO(ianh): Figure out how to localize this string tooltip: 'Open navigation menu' // TODO(ianh): Figure out how to localize this string
); );
} else { } else {
if (_canPop) if (canPop)
leading = _useCloseButton ? const CloseButton() : const BackButton(); leading = useCloseButton ? const CloseButton() : const BackButton();
} }
} }
if (leading != null) { if (leading != null) {
......
...@@ -308,7 +308,6 @@ void main() { ...@@ -308,7 +308,6 @@ void main() {
expect(find.text('A2'), findsOneWidget); expect(find.text('A2'), findsOneWidget);
}); });
testWidgets('AppBar render at zero size', (WidgetTester tester) async { testWidgets('AppBar render at zero size', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( new MaterialApp(
...@@ -760,4 +759,24 @@ void main() { ...@@ -760,4 +759,24 @@ void main() {
expect(appBarTop(tester), 0.0); expect(appBarTop(tester), 0.0);
expect(tester.getTopLeft(find.text('title')).dy, lessThan(100.0)); expect(tester.getTopLeft(find.text('title')).dy, lessThan(100.0));
}); });
testWidgets('AppBar updates when you add a drawer', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
home: new Scaffold(
appBar: new AppBar(),
),
),
);
expect(find.byIcon(Icons.menu), findsNothing);
await tester.pumpWidget(
new MaterialApp(
home: new Scaffold(
drawer: const Drawer(),
appBar: new AppBar(),
),
),
);
expect(find.byIcon(Icons.menu), findsOneWidget);
});
} }
...@@ -9,30 +9,62 @@ import 'finders.dart'; ...@@ -9,30 +9,62 @@ import 'finders.dart';
/// Asserts that the [Finder] matches no widgets in the widget tree. /// Asserts that the [Finder] matches no widgets in the widget tree.
/// ///
/// Example: /// ## Sample code
/// ///
/// ```dart
/// expect(find.text('Save'), findsNothing); /// expect(find.text('Save'), findsNothing);
/// ```
///
/// See also:
///
/// * [findsWidgets], when you want the finder to find one or more widgets.
/// * [findsOneWidgets], when you want the finder to find exactly one widget.
/// * [findsNWidgets], when you want the finder to find a specific number of widgets.
const Matcher findsNothing = const _FindsWidgetMatcher(null, 0); const Matcher findsNothing = const _FindsWidgetMatcher(null, 0);
/// Asserts that the [Finder] locates at least one widget in the widget tree. /// Asserts that the [Finder] locates at least one widget in the widget tree.
/// ///
/// Example: /// ## Sample code
/// ///
/// ```dart
/// expect(find.text('Save'), findsWidgets); /// expect(find.text('Save'), findsWidgets);
/// ```
///
/// See also:
///
/// * [findsNothing], when you want the finder to not find anything.
/// * [findsOneWidgets], when you want the finder to find exactly one widget.
/// * [findsNWidgets], when you want the finder to find a specific number of widgets.
const Matcher findsWidgets = const _FindsWidgetMatcher(1, null); const Matcher findsWidgets = const _FindsWidgetMatcher(1, null);
/// Asserts that the [Finder] locates at exactly one widget in the widget tree. /// Asserts that the [Finder] locates at exactly one widget in the widget tree.
/// ///
/// Example: /// ## Sample code
/// ///
/// ```dart
/// expect(find.text('Save'), findsOneWidget); /// expect(find.text('Save'), findsOneWidget);
/// ```
///
/// See also:
///
/// * [findsNothing], when you want the finder to not find anything.
/// * [findsWidgets], when you want the finder to find one or more widgets.
/// * [findsNWidgets], when you want the finder to find a specific number of widgets.
const Matcher findsOneWidget = const _FindsWidgetMatcher(1, 1); const Matcher findsOneWidget = const _FindsWidgetMatcher(1, 1);
/// Asserts that the [Finder] locates the specified number of widgets in the widget tree. /// Asserts that the [Finder] locates the specified number of widgets in the widget tree.
/// ///
/// Example: /// ## Sample code
/// ///
/// ```dart
/// expect(find.text('Save'), findsNWidgets(2)); /// expect(find.text('Save'), findsNWidgets(2));
/// ```
///
/// See also:
///
/// * [findsNothing], when you want the finder to not find anything.
/// * [findsWidgets], when you want the finder to find one or more widgets.
/// * [findsOneWidgets], when you want the finder to find exactly one widget.
Matcher findsNWidgets(int n) => new _FindsWidgetMatcher(n, n); Matcher findsNWidgets(int n) => new _FindsWidgetMatcher(n, n);
/// Asserts that the [Finder] locates the a single widget that has at /// Asserts that the [Finder] locates the a single widget that has at
...@@ -41,21 +73,41 @@ Matcher findsNWidgets(int n) => new _FindsWidgetMatcher(n, n); ...@@ -41,21 +73,41 @@ Matcher findsNWidgets(int n) => new _FindsWidgetMatcher(n, n);
/// It's important to use a full finder, since by default finders exclude /// It's important to use a full finder, since by default finders exclude
/// offstage widgets. /// offstage widgets.
/// ///
/// Example: /// ## Sample code
/// ///
/// ```dart
/// expect(find.text('Save', skipOffstage: false), isOffstage); /// expect(find.text('Save', skipOffstage: false), isOffstage);
/// ```
///
/// See also:
///
/// * [isOnStage], the opposite.
const Matcher isOffstage = const _IsOffstage(); const Matcher isOffstage = const _IsOffstage();
/// Asserts that the [Finder] locates the a single widget that has no /// Asserts that the [Finder] locates the a single widget that has no
/// [Offstage] widget ancestors. /// [Offstage] widget ancestors.
///
/// See also:
///
/// * [isOffStage], the opposite.
const Matcher isOnstage = const _IsOnstage(); const Matcher isOnstage = const _IsOnstage();
/// Asserts that the [Finder] locates the a single widget that has at /// Asserts that the [Finder] locates the a single widget that has at
/// least one [Card] widget ancestor. /// least one [Card] widget ancestor.
///
/// See also:
///
/// * [isNotInCard], the opposite.
const Matcher isInCard = const _IsInCard(); const Matcher isInCard = const _IsInCard();
/// Asserts that the [Finder] locates the a single widget that has no /// Asserts that the [Finder] locates the a single widget that has no
/// [Card] widget ancestors. /// [Card] widget ancestors.
///
/// This is equivalent to `isNot(isInCard)`.
///
/// See also:
///
/// * [isInCard], the opposite.
const Matcher isNotInCard = const _IsNotInCard(); const Matcher isNotInCard = const _IsNotInCard();
/// Asserts that an object's toString() is a plausible one-line description. /// Asserts that an object's toString() is a plausible one-line description.
...@@ -66,15 +118,47 @@ const Matcher isNotInCard = const _IsNotInCard(); ...@@ -66,15 +118,47 @@ const Matcher isNotInCard = const _IsNotInCard();
const Matcher hasOneLineDescription = const _HasOneLineDescription(); const Matcher hasOneLineDescription = const _HasOneLineDescription();
/// A matcher for functions that throw [FlutterError]. /// A matcher for functions that throw [FlutterError].
///
/// This is equivalent to `throwsA(const isInstanceOf<FlutterError>())`.
///
/// See also:
///
/// * [throwsAssertionError], to test if a function throws any [AssertionError].
/// * [isFlutterError], to test if any object is a [FlutterError].
/// * [isAssertionError], to test if any object is any kind of [AssertionError].
Matcher throwsFlutterError = throwsA(isFlutterError); Matcher throwsFlutterError = throwsA(isFlutterError);
/// A matcher for functions that throw [AssertionError]. /// A matcher for functions that throw [AssertionError].
///
/// This is equivalent to `throwsA(const isInstanceOf<AssertionError>())`.
///
/// See also:
///
/// * [throwsFlutterError], to test if a function throws a [FlutterError].
/// * [isFlutterError], to test if any object is a [FlutterError].
/// * [isAssertionError], to test if any object is any kind of [AssertionError].
Matcher throwsAssertionError = throwsA(isAssertionError); Matcher throwsAssertionError = throwsA(isAssertionError);
/// A matcher for [FlutterError]. /// A matcher for [FlutterError].
///
/// This is equivalent to `const isInstanceOf<FlutterError>()`.
///
/// See also:
///
/// * [throwsFlutterError], to test if a function throws a [FlutterError].
/// * [throwsAssertionError], to test if a function throws any [AssertionError].
/// * [isAssertionError], to test if any object is any kind of [AssertionError].
const Matcher isFlutterError = const isInstanceOf<FlutterError>(); const Matcher isFlutterError = const isInstanceOf<FlutterError>();
/// A matcher for [AssertionError]. /// A matcher for [AssertionError].
///
/// This is equivalent to `const isInstanceOf<AssertionError>()`.
///
/// See also:
///
/// * [throwsFlutterError], to test if a function throws a [FlutterError].
/// * [throwsAssertionError], to test if a function throws any [AssertionError].
/// * [isFlutterError], to test if any object is a [FlutterError].
const Matcher isAssertionError = const isInstanceOf<AssertionError>(); const Matcher isAssertionError = const isInstanceOf<AssertionError>();
/// Asserts that two [double]s are equal, within some tolerated error. /// Asserts that two [double]s are equal, within some tolerated error.
...@@ -84,6 +168,13 @@ const Matcher isAssertionError = const isInstanceOf<AssertionError>(); ...@@ -84,6 +168,13 @@ const Matcher isAssertionError = const isInstanceOf<AssertionError>();
/// using the `epsilon` argument. This matcher is intended to compare floating /// using the `epsilon` argument. This matcher is intended to compare floating
/// point numbers that are the result of different sequences of operations, such /// point numbers that are the result of different sequences of operations, such
/// that they may have accumulated slightly different errors. /// that they may have accumulated slightly different errors.
///
/// See also:
///
/// * [closeTo], which is identical except that the epsilon argument is
/// required and not named.
/// * [isInclusiveRange], which matches if the argument is in a specified
/// range.
Matcher moreOrLessEquals(double value, { double epsilon: 1e-10 }) { Matcher moreOrLessEquals(double value, { double epsilon: 1e-10 }) {
return new _MoreOrLessEquals(value, epsilon); return new _MoreOrLessEquals(value, epsilon);
} }
......
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