mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Refactor IconThemeData.fallback (#7490)
Now IconThemeData.fallback is a factory constructor and IconThemeData.of() does the work of computing the fallback for its clients. Also, add tests for ImageIcon and ListItems.
This commit is contained in:
parent
3150e3fbd4
commit
4955eef8a7
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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<Widget> divideItems({ BuildContext context, Iterable<Widget> items, Color color }) sync* {
|
||||
static Iterable<Widget> divideItems({ BuildContext context, @required Iterable<Widget> items, Color color }) sync* {
|
||||
assert(items != null);
|
||||
assert(color != null || context != null);
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
50
packages/flutter/test/material/list_item_test.dart
Normal file
50
packages/flutter/test/material/list_item_test.dart
Normal file
@ -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<String> titles = <String>[ '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);
|
||||
});
|
||||
}
|
||||
@ -37,18 +37,13 @@ class _TimePickerLauncher extends StatelessWidget {
|
||||
Future<Point> startPicker(WidgetTester tester, ValueChanged<TimeOfDay> 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<Null> 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() {
|
||||
|
||||
@ -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));
|
||||
|
||||
});
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -23,8 +23,8 @@ class TestImageInfo implements ImageInfo {
|
||||
String toString() => '$runtimeType($value)';
|
||||
}
|
||||
|
||||
class TestProvider extends ImageProvider<int> {
|
||||
const TestProvider(this.key, this.imageValue);
|
||||
class TestImageProvider extends ImageProvider<int> {
|
||||
const TestImageProvider(this.key, this.imageValue);
|
||||
final int key;
|
||||
final int imageValue;
|
||||
|
||||
@ -52,4 +52,4 @@ Future<ImageInfo> extractOneFrame(ImageStream stream) {
|
||||
}
|
||||
stream.addListener(listener);
|
||||
return completer.future;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user