Commit 508b8c46 authored by Dragoș Tiselice's avatar Dragoș Tiselice Committed by GitHub

Added a flag to divide Material slices. (#5402)

This commit changes MergeableMaterial to include a flag that
specifies whether connected Material slices should have dividers
between them.
parent a010d6eb
...@@ -82,6 +82,7 @@ class MergeableMaterial extends StatefulWidget { ...@@ -82,6 +82,7 @@ class MergeableMaterial extends StatefulWidget {
Key key, Key key,
this.mainAxis: Axis.vertical, this.mainAxis: Axis.vertical,
this.elevation: 2, this.elevation: 2,
this.hasDividers: false,
this.children: const <MergeableMaterialItem>[] this.children: const <MergeableMaterialItem>[]
}) : super(key: key); }) : super(key: key);
...@@ -94,6 +95,9 @@ class MergeableMaterial extends StatefulWidget { ...@@ -94,6 +95,9 @@ class MergeableMaterial extends StatefulWidget {
/// The elevation of all the [Material] slices. /// The elevation of all the [Material] slices.
final int elevation; final int elevation;
/// Whether connected pieces of [MaterialSlice] have dividers between them.
final bool hasDividers;
@override @override
String toString() { String toString() {
return 'MergeableMaterial(' return 'MergeableMaterial('
...@@ -234,6 +238,21 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -234,6 +238,21 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
return false; return false;
} }
void _removeEmptyGaps() {
int j = 0;
while (j < _children.length) {
if (
_children[j] is MaterialGap &&
_animationTuples[_children[j].key].controller.status == AnimationStatus.dismissed
) {
_removeChild(j);
} else {
j += 1;
}
}
}
@override @override
void didUpdateConfig(MergeableMaterial oldConfig) { void didUpdateConfig(MergeableMaterial oldConfig) {
super.didUpdateConfig(oldConfig); super.didUpdateConfig(oldConfig);
...@@ -253,17 +272,7 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -253,17 +272,7 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
assert(_debugGapsAreValid(newChildren)); assert(_debugGapsAreValid(newChildren));
while (j < _children.length) { _removeEmptyGaps();
if (_children[j] is MaterialGap &&
_animationTuples[_children[j].key].controller.status
== AnimationStatus.dismissed) {
_removeChild(j);
} else {
j += 1;
}
}
j = 0;
while (i < newChildren.length && j < _children.length) { while (i < newChildren.length && j < _children.length) {
if (newOnly.contains(newChildren[i].key) || if (newOnly.contains(newChildren[i].key) ||
...@@ -471,10 +480,37 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -471,10 +480,37 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
); );
} }
List<Widget> _divideSlices(BuildContext context, List<Widget> slices, List<Key> keys) {
if (config.hasDividers) {
List<Widget> divided = <Widget>[];
for (int i = 0; i < slices.length; i += 1) {
divided.add(
new DecoratedBox(
key: keys[i],
decoration: i != slices.length - 1 ? new BoxDecoration(
border: new Border(
bottom: new BorderSide(color: Theme.of(context).dividerColor)
)
) : new BoxDecoration(),
child: slices[i]
)
);
}
return divided;
} else {
return slices;
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
_removeEmptyGaps();
final List<Widget> widgets = <Widget>[]; final List<Widget> widgets = <Widget>[];
List<Widget> slices = <Widget>[]; List<Widget> slices = <Widget>[];
List<Key> keys = <Key>[];
int i; int i;
for (i = 0; i < _children.length; i += 1) { for (i = 0; i < _children.length; i += 1) {
...@@ -489,11 +525,12 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -489,11 +525,12 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
), ),
child: new BlockBody( child: new BlockBody(
mainAxis: config.mainAxis, mainAxis: config.mainAxis,
children: slices children: _divideSlices(context, slices, keys)
) )
) )
); );
slices = <Widget>[]; slices = <Widget>[];
keys = <Key>[];
widgets.add( widgets.add(
new SizedBox( new SizedBox(
...@@ -506,14 +543,11 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -506,14 +543,11 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
slices.add( slices.add(
new Material( new Material(
// Since slices live in different Material widgets, the parent
// hierarchy can change and lead to the slice being rebuilt. Using
// a global key solves the issue.
key: new _MergeableMaterialSliceKey(_children[i].key),
type: MaterialType.transparency, type: MaterialType.transparency,
child: slice.child child: slice.child
) )
); );
keys.add(new _MergeableMaterialSliceKey(_children[i].key));
} }
} }
...@@ -527,11 +561,12 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -527,11 +561,12 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
), ),
child: new BlockBody( child: new BlockBody(
mainAxis: config.mainAxis, mainAxis: config.mainAxis,
children: slices children: _divideSlices(context, slices, keys)
) )
) )
); );
slices = <Widget>[]; slices = <Widget>[];
keys = <Key>[];
} }
return new _MergeableMaterialBlockBody( return new _MergeableMaterialBlockBody(
...@@ -543,6 +578,8 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -543,6 +578,8 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
} }
} }
// The parent hierarchy can change and lead to the slice being
// rebuilt. Usinga global key solves the issue.
class _MergeableMaterialSliceKey extends GlobalKey { class _MergeableMaterialSliceKey extends GlobalKey {
const _MergeableMaterialSliceKey(this.value) : super.constructor(); const _MergeableMaterialSliceKey(this.value) : super.constructor();
...@@ -558,6 +595,11 @@ class _MergeableMaterialSliceKey extends GlobalKey { ...@@ -558,6 +595,11 @@ class _MergeableMaterialSliceKey extends GlobalKey {
@override @override
int get hashCode => value.hashCode; int get hashCode => value.hashCode;
@override
String toString() {
return '_MergeableMaterialSliceKey($value)';
}
} }
class _MergeableMaterialBlockBody extends BlockBody { class _MergeableMaterialBlockBody extends BlockBody {
......
...@@ -1014,4 +1014,116 @@ void main() { ...@@ -1014,4 +1014,116 @@ void main() {
matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Round); matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Round);
matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round); matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round);
}); });
bool isDivider(Widget widget) {
final DecoratedBox box = widget;
return box.decoration == new BoxDecoration(
border: new Border(
bottom: new BorderSide(color: const Color(0x1F000000))
)
);
}
testWidgets('MergeableMaterial dividers', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
child: new MergeableMaterial(
hasDividers: true,
children: <MergeableMaterialItem>[
new MaterialSlice(
key: new ValueKey<String>('A'),
child: new SizedBox(
width: 100.0,
height: 100.0
)
),
new MaterialSlice(
key: new ValueKey<String>('B'),
child: new SizedBox(
width: 100.0,
height: 100.0
)
),
new MaterialSlice(
key: new ValueKey<String>('C'),
child: new SizedBox(
width: 100.0,
height: 100.0
)
),
new MaterialSlice(
key: new ValueKey<String>('D'),
child: new SizedBox(
width: 100.0,
height: 100.0
)
)
]
)
)
)
);
List<Widget> boxes = tester.widgetList(find.byType(DecoratedBox)).toList();
int offset = 3;
expect(isDivider(boxes[offset]), isTrue);
expect(isDivider(boxes[offset + 1]), isTrue);
expect(isDivider(boxes[offset + 2]), isTrue);
expect(isDivider(boxes[offset + 3]), isFalse);
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
child: new MergeableMaterial(
hasDividers: true,
children: <MergeableMaterialItem>[
new MaterialSlice(
key: new ValueKey<String>('A'),
child: new SizedBox(
width: 100.0,
height: 100.0
)
),
new MaterialSlice(
key: new ValueKey<String>('B'),
child: new SizedBox(
width: 100.0,
height: 100.0
)
),
new MaterialGap(
key: new ValueKey<String>('x')
),
new MaterialSlice(
key: new ValueKey<String>('C'),
child: new SizedBox(
width: 100.0,
height: 100.0
)
),
new MaterialSlice(
key: new ValueKey<String>('D'),
child: new SizedBox(
width: 100.0,
height: 100.0
)
)
]
)
)
)
);
boxes = tester.widgetList(find.byType(DecoratedBox)).toList();
offset = 3;
expect(isDivider(boxes[offset]), isTrue);
expect(isDivider(boxes[offset + 1]), isFalse);
// offset + 2 is gap
expect(isDivider(boxes[offset + 3]), isTrue);
expect(isDivider(boxes[offset + 4]), isFalse);
});
} }
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