diff --git a/examples/flutter_gallery/lib/demo/bottom_navigation_demo.dart b/examples/flutter_gallery/lib/demo/bottom_navigation_demo.dart index 191829045bb..7778375b53e 100644 --- a/examples/flutter_gallery/lib/demo/bottom_navigation_demo.dart +++ b/examples/flutter_gallery/lib/demo/bottom_navigation_demo.dart @@ -66,7 +66,7 @@ class NavigationIconView { class CustomIcon extends StatelessWidget { @override Widget build(BuildContext context) { - final IconThemeData iconTheme = IconTheme.of(context).fallback(); + final IconThemeData iconTheme = IconTheme.of(context); return new Container( margin: const EdgeInsets.all(4.0), width: iconTheme.size - 8.0, diff --git a/packages/flutter/lib/src/material/flutter_logo.dart b/packages/flutter/lib/src/material/flutter_logo.dart index 18617a43cdb..9d073ca1e58 100644 --- a/packages/flutter/lib/src/material/flutter_logo.dart +++ b/packages/flutter/lib/src/material/flutter_logo.dart @@ -73,7 +73,7 @@ class FlutterLogo extends StatelessWidget { @override Widget build(BuildContext context) { - final IconThemeData iconTheme = IconTheme.of(context).fallback(); + final IconThemeData iconTheme = IconTheme.of(context); final double iconSize = size ?? iconTheme.size; return new AnimatedContainer( width: iconSize, diff --git a/packages/flutter/lib/src/material/icon.dart b/packages/flutter/lib/src/material/icon.dart index 8f7113e7876..dbeaaad6f85 100644 --- a/packages/flutter/lib/src/material/icon.dart +++ b/packages/flutter/lib/src/material/icon.dart @@ -80,7 +80,7 @@ class Icon extends StatelessWidget { @override Widget build(BuildContext context) { - final IconThemeData iconTheme = IconTheme.of(context).fallback(); + final IconThemeData iconTheme = IconTheme.of(context); final double iconSize = size ?? iconTheme.size; diff --git a/packages/flutter/lib/src/material/icon_theme.dart b/packages/flutter/lib/src/material/icon_theme.dart index 874aa91d94e..0517901df88 100644 --- a/packages/flutter/lib/src/material/icon_theme.dart +++ b/packages/flutter/lib/src/material/icon_theme.dart @@ -37,7 +37,7 @@ class IconTheme extends InheritedWidget { }) { return new IconTheme( key: key, - data: IconTheme.of(context).merge(data), + data: _getInheritedIconThemData(context).merge(data), child: child ); } @@ -56,8 +56,13 @@ class IconTheme extends InheritedWidget { /// IconThemeData theme = IconTheme.of(context); /// ``` static IconThemeData of(BuildContext context) { - IconTheme result = context.inheritFromWidgetOfExactType(IconTheme); - return result?.data ?? Theme.of(context).iconTheme; + IconThemeData iconThemeData = _getInheritedIconThemData(context); + return iconThemeData.isConcrete ? iconThemeData : const IconThemeData.fallback().merge(iconThemeData); + } + + static IconThemeData _getInheritedIconThemData(BuildContext context) { + IconTheme iconTheme = context.inheritFromWidgetOfExactType(IconTheme); + return iconTheme?.data ?? Theme.of(context).iconTheme; } @override diff --git a/packages/flutter/lib/src/material/icon_theme_data.dart b/packages/flutter/lib/src/material/icon_theme_data.dart index 416b2de0ad0..78e197ec5d9 100644 --- a/packages/flutter/lib/src/material/icon_theme_data.dart +++ b/packages/flutter/lib/src/material/icon_theme_data.dart @@ -19,6 +19,14 @@ class IconThemeData { /// is clamped between 0.0 and 1.0. const IconThemeData({ this.color, double opacity, this.size }) : _opacity = opacity; + /// Creates an icon them with some reasonable default values. + /// + /// The [color] is black, the [opacity] is 1.0, and the [size] is 24.0. + const IconThemeData.fallback() + : color = const Color(0xFF000000), + _opacity = 1.0, + size = 24.0; + /// Creates a copy of this icon theme but with the given fields replaced with /// the new values. IconThemeData copyWith({ Color color, double opacity, double size }) { @@ -42,22 +50,8 @@ class IconThemeData { ); } - /// Creates an icon theme that is identical to this icon theme but with - /// any null fields filled in. Specific fallbacks can be given, but in their - /// absence, this method defaults to black, fully opaque, and size 24.0. - IconThemeData fallback({ - Color color: const Color(0xFF000000), - double opacity: 1.0, - double size: 24.0 - }) { - if (this.color != null && this.opacity != null && this.size != null) - return this; - return new IconThemeData( - color: this.color ?? color, - opacity: this.opacity ?? opacity, - size: this.size ?? size - ); - } + /// Whether all the properties of this object are non-null. + bool get isConcrete => color != null && opacity != null && size != null; /// The default color for icons. final Color color; diff --git a/packages/flutter/lib/src/material/image_icon.dart b/packages/flutter/lib/src/material/image_icon.dart index 1afa30a4318..3d689f1a731 100644 --- a/packages/flutter/lib/src/material/image_icon.dart +++ b/packages/flutter/lib/src/material/image_icon.dart @@ -53,7 +53,7 @@ class ImageIcon extends StatelessWidget { @override Widget build(BuildContext context) { - final IconThemeData iconTheme = IconTheme.of(context).fallback(); + final IconThemeData iconTheme = IconTheme.of(context); final double iconSize = size ?? iconTheme.size; diff --git a/packages/flutter/lib/src/material/list_item.dart b/packages/flutter/lib/src/material/list_item.dart index dad06c006c5..3050924964c 100644 --- a/packages/flutter/lib/src/material/list_item.dart +++ b/packages/flutter/lib/src/material/list_item.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:flutter/widgets.dart'; +import 'package:meta/meta.dart'; import 'constants.dart'; import 'debug.dart'; @@ -104,7 +105,7 @@ class ListItem extends StatelessWidget { /// See also: /// /// * [Divider], which you can use to obtain this effect manually. - static Iterable divideItems({ BuildContext context, Iterable items, Color color }) sync* { + static Iterable divideItems({ BuildContext context, @required Iterable items, Color color }) sync* { assert(items != null); assert(color != null || context != null); diff --git a/packages/flutter/test/material/bottom_navigation_bar_test.dart b/packages/flutter/test/material/bottom_navigation_bar_test.dart index ab119e15a85..b91a5df6723 100644 --- a/packages/flutter/test/material/bottom_navigation_bar_test.dart +++ b/packages/flutter/test/material/bottom_navigation_bar_test.dart @@ -256,7 +256,7 @@ void main() { title: new Text('B'), icon: new Builder( builder: (BuildContext context) { - builderIconSize = IconTheme.of(context).fallback().size; + builderIconSize = IconTheme.of(context).size; return new SizedBox( width: builderIconSize, height: builderIconSize, diff --git a/packages/flutter/test/material/image_icon_test.dart b/packages/flutter/test/material/image_icon_test.dart index 783f9eae120..d749acd7332 100644 --- a/packages/flutter/test/material/image_icon_test.dart +++ b/packages/flutter/test/material/image_icon_test.dart @@ -4,19 +4,40 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import '../services/mocks_for_image_cache.dart'; + +const ImageProvider _kImage = const TestImageProvider(21, 42); + void main() { testWidgets('ImageIcon sizing - no theme, default size', (WidgetTester tester) async { await tester.pumpWidget( new Center( - child: const ImageIcon(null) + child: const ImageIcon(_kImage) ) ); RenderBox renderObject = tester.renderObject(find.byType(ImageIcon)); expect(renderObject.size, equals(const Size.square(24.0))); + expect(find.byType(Image), findsOneWidget); + }); + + testWidgets('Icon opacity', (WidgetTester tester) async { + await tester.pumpWidget( + new Center( + child: new IconTheme( + data: new IconThemeData(opacity: 0.5), + child: const ImageIcon(_kImage), + ), + ), + ); + + Image image = tester.widget(find.byType(Image)); + expect(image, isNotNull); + expect(image.color.alpha, equals(128)); }); testWidgets('ImageIcon sizing - no theme, explicit size', (WidgetTester tester) async { diff --git a/packages/flutter/test/material/list_item_test.dart b/packages/flutter/test/material/list_item_test.dart new file mode 100644 index 00000000000..eb586097ba1 --- /dev/null +++ b/packages/flutter/test/material/list_item_test.dart @@ -0,0 +1,50 @@ +// Copyright 2015 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('ListItem control test', (WidgetTester tester) async { + await tester.pumpWidget(new MaterialApp( + home: new Material( + child: new Center( + child: new ListItem( + leading: new Icon(Icons.thumb_up), + title: new Text('Title'), + subtitle: new Text('Subtitle'), + trailing: new Icon(Icons.info), + enabled: false, + ), + ), + ), + )); + + expect(find.text('Title'), findsOneWidget); + expect(find.text('Subtitle'), findsOneWidget); + }); + + testWidgets('ListItem control test', (WidgetTester tester) async { + List titles = [ 'first', 'second', 'third' ]; + + await tester.pumpWidget(new MaterialApp( + home: new Material( + child: new Builder( + builder: (BuildContext context) { + return new Block( + children: ListItem.divideItems( + context: context, + items: titles.map((String title) => new ListItem(title: new Text(title))), + ).toList(), + ); + }, + ), + ), + )); + + expect(find.text('first'), findsOneWidget); + expect(find.text('second'), findsOneWidget); + expect(find.text('third'), findsOneWidget); + }); +} diff --git a/packages/flutter/test/material/time_picker_test.dart b/packages/flutter/test/material/time_picker_test.dart index 0818e331e47..5b3b799b974 100644 --- a/packages/flutter/test/material/time_picker_test.dart +++ b/packages/flutter/test/material/time_picker_test.dart @@ -37,18 +37,13 @@ class _TimePickerLauncher extends StatelessWidget { Future startPicker(WidgetTester tester, ValueChanged onChanged) async { await tester.pumpWidget(new _TimePickerLauncher(onChanged: onChanged)); await tester.tap(find.text('X')); - - await tester.pump(); // start animation - await tester.pump(const Duration(seconds: 1)); - + await tester.pumpUntilNoTransientCallbacks(const Duration(seconds: 1)); return tester.getCenter(find.byKey(new Key('time-picker-dial'))); } Future finishPicker(WidgetTester tester) async { await tester.tap(find.text('OK')); - - await tester.pump(); // start animation - await tester.pump(const Duration(seconds: 1)); + await tester.pumpUntilNoTransientCallbacks(const Duration(seconds: 1)); } void main() { diff --git a/packages/flutter/test/services/image_cache_resize_test.dart b/packages/flutter/test/services/image_cache_resize_test.dart index 8352be85e15..b1d21edd774 100644 --- a/packages/flutter/test/services/image_cache_resize_test.dart +++ b/packages/flutter/test/services/image_cache_resize_test.dart @@ -12,10 +12,10 @@ void main() { imageCache.maximumSize = 2; - TestImageInfo a = await extractOneFrame(const TestProvider(1, 1).resolve(ImageConfiguration.empty)); - TestImageInfo b = await extractOneFrame(const TestProvider(2, 2).resolve(ImageConfiguration.empty)); - TestImageInfo c = await extractOneFrame(const TestProvider(3, 3).resolve(ImageConfiguration.empty)); - TestImageInfo d = await extractOneFrame(const TestProvider(1, 4).resolve(ImageConfiguration.empty)); + TestImageInfo a = await extractOneFrame(const TestImageProvider(1, 1).resolve(ImageConfiguration.empty)); + TestImageInfo b = await extractOneFrame(const TestImageProvider(2, 2).resolve(ImageConfiguration.empty)); + TestImageInfo c = await extractOneFrame(const TestImageProvider(3, 3).resolve(ImageConfiguration.empty)); + TestImageInfo d = await extractOneFrame(const TestImageProvider(1, 4).resolve(ImageConfiguration.empty)); expect(a.value, equals(1)); expect(b.value, equals(2)); expect(c.value, equals(3)); @@ -23,18 +23,18 @@ void main() { imageCache.maximumSize = 0; - TestImageInfo e = await extractOneFrame(const TestProvider(1, 5).resolve(ImageConfiguration.empty)); + TestImageInfo e = await extractOneFrame(const TestImageProvider(1, 5).resolve(ImageConfiguration.empty)); expect(e.value, equals(5)); - TestImageInfo f = await extractOneFrame(const TestProvider(1, 6).resolve(ImageConfiguration.empty)); + TestImageInfo f = await extractOneFrame(const TestImageProvider(1, 6).resolve(ImageConfiguration.empty)); expect(f.value, equals(6)); imageCache.maximumSize = 3; - TestImageInfo g = await extractOneFrame(const TestProvider(1, 7).resolve(ImageConfiguration.empty)); + TestImageInfo g = await extractOneFrame(const TestImageProvider(1, 7).resolve(ImageConfiguration.empty)); expect(g.value, equals(7)); - TestImageInfo h = await extractOneFrame(const TestProvider(1, 8).resolve(ImageConfiguration.empty)); + TestImageInfo h = await extractOneFrame(const TestImageProvider(1, 8).resolve(ImageConfiguration.empty)); expect(h.value, equals(7)); }); diff --git a/packages/flutter/test/services/image_cache_test.dart b/packages/flutter/test/services/image_cache_test.dart index 0db074f93ab..a9acf223078 100644 --- a/packages/flutter/test/services/image_cache_test.dart +++ b/packages/flutter/test/services/image_cache_test.dart @@ -12,69 +12,69 @@ void main() { imageCache.maximumSize = 3; - TestImageInfo a = await extractOneFrame(const TestProvider(1, 1).resolve(ImageConfiguration.empty)); + TestImageInfo a = await extractOneFrame(const TestImageProvider(1, 1).resolve(ImageConfiguration.empty)); expect(a.value, equals(1)); - TestImageInfo b = await extractOneFrame(const TestProvider(1, 2).resolve(ImageConfiguration.empty)); + TestImageInfo b = await extractOneFrame(const TestImageProvider(1, 2).resolve(ImageConfiguration.empty)); expect(b.value, equals(1)); - TestImageInfo c = await extractOneFrame(const TestProvider(1, 3).resolve(ImageConfiguration.empty)); + TestImageInfo c = await extractOneFrame(const TestImageProvider(1, 3).resolve(ImageConfiguration.empty)); expect(c.value, equals(1)); - TestImageInfo d = await extractOneFrame(const TestProvider(1, 4).resolve(ImageConfiguration.empty)); + TestImageInfo d = await extractOneFrame(const TestImageProvider(1, 4).resolve(ImageConfiguration.empty)); expect(d.value, equals(1)); - TestImageInfo e = await extractOneFrame(const TestProvider(1, 5).resolve(ImageConfiguration.empty)); + TestImageInfo e = await extractOneFrame(const TestImageProvider(1, 5).resolve(ImageConfiguration.empty)); expect(e.value, equals(1)); - TestImageInfo f = await extractOneFrame(const TestProvider(1, 6).resolve(ImageConfiguration.empty)); + TestImageInfo f = await extractOneFrame(const TestImageProvider(1, 6).resolve(ImageConfiguration.empty)); expect(f.value, equals(1)); expect(f, equals(a)); // cache still only has one entry in it: 1(1) - TestImageInfo g = await extractOneFrame(const TestProvider(2, 7).resolve(ImageConfiguration.empty)); + TestImageInfo g = await extractOneFrame(const TestImageProvider(2, 7).resolve(ImageConfiguration.empty)); expect(g.value, equals(7)); // cache has two entries in it: 1(1), 2(7) - TestImageInfo h = await extractOneFrame(const TestProvider(1, 8).resolve(ImageConfiguration.empty)); + TestImageInfo h = await extractOneFrame(const TestImageProvider(1, 8).resolve(ImageConfiguration.empty)); expect(h.value, equals(1)); // cache still has two entries in it: 2(7), 1(1) - TestImageInfo i = await extractOneFrame(const TestProvider(3, 9).resolve(ImageConfiguration.empty)); + TestImageInfo i = await extractOneFrame(const TestImageProvider(3, 9).resolve(ImageConfiguration.empty)); expect(i.value, equals(9)); // cache has three entries in it: 2(7), 1(1), 3(9) - TestImageInfo j = await extractOneFrame(const TestProvider(1, 10).resolve(ImageConfiguration.empty)); + TestImageInfo j = await extractOneFrame(const TestImageProvider(1, 10).resolve(ImageConfiguration.empty)); expect(j.value, equals(1)); // cache still has three entries in it: 2(7), 3(9), 1(1) - TestImageInfo k = await extractOneFrame(const TestProvider(4, 11).resolve(ImageConfiguration.empty)); + TestImageInfo k = await extractOneFrame(const TestImageProvider(4, 11).resolve(ImageConfiguration.empty)); expect(k.value, equals(11)); // cache has three entries: 3(9), 1(1), 4(11) - TestImageInfo l = await extractOneFrame(const TestProvider(1, 12).resolve(ImageConfiguration.empty)); + TestImageInfo l = await extractOneFrame(const TestImageProvider(1, 12).resolve(ImageConfiguration.empty)); expect(l.value, equals(1)); // cache has three entries: 3(9), 4(11), 1(1) - TestImageInfo m = await extractOneFrame(const TestProvider(2, 13).resolve(ImageConfiguration.empty)); + TestImageInfo m = await extractOneFrame(const TestImageProvider(2, 13).resolve(ImageConfiguration.empty)); expect(m.value, equals(13)); // cache has three entries: 4(11), 1(1), 2(13) - TestImageInfo n = await extractOneFrame(const TestProvider(3, 14).resolve(ImageConfiguration.empty)); + TestImageInfo n = await extractOneFrame(const TestImageProvider(3, 14).resolve(ImageConfiguration.empty)); expect(n.value, equals(14)); // cache has three entries: 1(1), 2(13), 3(14) - TestImageInfo o = await extractOneFrame(const TestProvider(4, 15).resolve(ImageConfiguration.empty)); + TestImageInfo o = await extractOneFrame(const TestImageProvider(4, 15).resolve(ImageConfiguration.empty)); expect(o.value, equals(15)); // cache has three entries: 2(13), 3(14), 4(15) - TestImageInfo p = await extractOneFrame(const TestProvider(1, 16).resolve(ImageConfiguration.empty)); + TestImageInfo p = await extractOneFrame(const TestImageProvider(1, 16).resolve(ImageConfiguration.empty)); expect(p.value, equals(16)); // cache has three entries: 3(14), 4(15), 1(16) diff --git a/packages/flutter/test/services/mocks_for_image_cache.dart b/packages/flutter/test/services/mocks_for_image_cache.dart index b0652a76db3..7c25adbcf26 100644 --- a/packages/flutter/test/services/mocks_for_image_cache.dart +++ b/packages/flutter/test/services/mocks_for_image_cache.dart @@ -23,8 +23,8 @@ class TestImageInfo implements ImageInfo { String toString() => '$runtimeType($value)'; } -class TestProvider extends ImageProvider { - const TestProvider(this.key, this.imageValue); +class TestImageProvider extends ImageProvider { + const TestImageProvider(this.key, this.imageValue); final int key; final int imageValue; @@ -52,4 +52,4 @@ Future extractOneFrame(ImageStream stream) { } stream.addListener(listener); return completer.future; -} \ No newline at end of file +}