Commit 826d87a9 authored by Dragoș Tiselice's avatar Dragoș Tiselice Committed by GitHub

Made MergeableMaterial dividers animated seamlessly. (#5857)

Changed MergableMaterial to animated between states that have dividers
and those that don't in order not to have dividers that appear
unanimatedly. Fixes #5847.
parent f4904b14
...@@ -228,7 +228,7 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -228,7 +228,7 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
_animationTuples[child.key] = null; _animationTuples[child.key] = null;
} }
bool _closingGap(int index) { bool _isClosingGap(int index) {
if (index < _children.length - 1 && _children[index] is MaterialGap) { if (index < _children.length - 1 && _children[index] is MaterialGap) {
return _animationTuples[_children[index].key].controller.status == return _animationTuples[_children[index].key].controller.status ==
AnimationStatus.reverse; AnimationStatus.reverse;
...@@ -284,7 +284,7 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -284,7 +284,7 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
i += 1; i += 1;
// Skip old keys. // Skip old keys.
while (oldOnly.contains(_children[j].key) || _closingGap(j)) while (oldOnly.contains(_children[j].key) || _isClosingGap(j))
j += 1; j += 1;
final int newLength = i - startNew; final int newLength = i - startNew;
...@@ -479,28 +479,12 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -479,28 +479,12 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
); );
} }
List<Widget> _divideSlices(BuildContext context, List<Widget> slices, List<Key> keys) { bool _willNeedDivider(int index) {
if (config.hasDividers) { if (index < 0)
List<Widget> divided = <Widget>[]; return false;
if (index >= _children.length)
for (int i = 0; i < slices.length; i += 1) { return false;
divided.add( return _children[index] is MaterialSlice || _isClosingGap(index);
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
...@@ -509,7 +493,6 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -509,7 +493,6 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
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) {
...@@ -524,12 +507,11 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -524,12 +507,11 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
), ),
child: new BlockBody( child: new BlockBody(
mainAxis: config.mainAxis, mainAxis: config.mainAxis,
children: _divideSlices(context, slices, keys) children: slices
) )
) )
); );
slices = <Widget>[]; slices = <Widget>[];
keys = <Key>[];
widgets.add( widgets.add(
new SizedBox( new SizedBox(
...@@ -539,14 +521,50 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -539,14 +521,50 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
); );
} else { } else {
MaterialSlice slice = _children[i]; MaterialSlice slice = _children[i];
Widget child = slice.child;
if (config.hasDividers) {
final bool hasTopDivider = _willNeedDivider(i - 1);
final bool hasBottomDivider = _willNeedDivider(i + 1);
Border border;
final BorderSide divider = new BorderSide(
color: Theme.of(context).dividerColor,
width: 0.5
);
if (i == 0) {
border = new Border(
bottom: hasBottomDivider ? divider : BorderSide.none
);
} else if (i == _children.length - 1) {
border = new Border(
top: hasTopDivider ? divider : BorderSide.none
);
} else {
border = new Border(
top: hasTopDivider ? divider : BorderSide.none,
bottom: hasBottomDivider ? divider : BorderSide.none
);
}
assert(border != null);
child = new AnimatedContainer(
key: new _MergeableMaterialSliceKey(_children[i].key),
decoration: new BoxDecoration(border: border),
duration: kThemeAnimationDuration,
curve: Curves.fastOutSlowIn,
child: child
);
}
slices.add( slices.add(
new Material( new Material(
type: MaterialType.transparency, type: MaterialType.transparency,
child: slice.child child: child
) )
); );
keys.add(new _MergeableMaterialSliceKey(_children[i].key));
} }
} }
...@@ -560,12 +578,11 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -560,12 +578,11 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
), ),
child: new BlockBody( child: new BlockBody(
mainAxis: config.mainAxis, mainAxis: config.mainAxis,
children: _divideSlices(context, slices, keys) children: slices
) )
) )
); );
slices = <Widget>[]; slices = <Widget>[];
keys = <Key>[];
} }
return new _MergeableMaterialBlockBody( return new _MergeableMaterialBlockBody(
...@@ -578,7 +595,7 @@ class _MergeableMaterialState extends State<MergeableMaterial> { ...@@ -578,7 +595,7 @@ class _MergeableMaterialState extends State<MergeableMaterial> {
} }
// The parent hierarchy can change and lead to the slice being // The parent hierarchy can change and lead to the slice being
// rebuilt. Usinga global key solves the issue. // rebuilt. Using a 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();
......
...@@ -1014,12 +1014,14 @@ void main() { ...@@ -1014,12 +1014,14 @@ void main() {
matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round); matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round);
}); });
bool isDivider(Widget widget) { bool isDivider(Widget widget, bool top, bool bottom) {
final DecoratedBox box = widget; final DecoratedBox box = widget;
final BorderSide side = new BorderSide(color: const Color(0x1F000000), width: 0.5);
return box.decoration == new BoxDecoration( return box.decoration == new BoxDecoration(
border: new Border( border: new Border(
bottom: new BorderSide(color: const Color(0x1F000000)) top: top ? side : BorderSide.none,
bottom: bottom ? side : BorderSide.none
) )
); );
} }
...@@ -1068,10 +1070,10 @@ void main() { ...@@ -1068,10 +1070,10 @@ void main() {
List<Widget> boxes = tester.widgetList(find.byType(DecoratedBox)).toList(); List<Widget> boxes = tester.widgetList(find.byType(DecoratedBox)).toList();
int offset = 3; int offset = 3;
expect(isDivider(boxes[offset]), isTrue); expect(isDivider(boxes[offset], false, true), isTrue);
expect(isDivider(boxes[offset + 1]), isTrue); expect(isDivider(boxes[offset + 1], true, true), isTrue);
expect(isDivider(boxes[offset + 2]), isTrue); expect(isDivider(boxes[offset + 2], true, true), isTrue);
expect(isDivider(boxes[offset + 3]), isFalse); expect(isDivider(boxes[offset + 3], true, false), isTrue);
await tester.pumpWidget( await tester.pumpWidget(
new Scaffold( new Scaffold(
...@@ -1116,13 +1118,16 @@ void main() { ...@@ -1116,13 +1118,16 @@ void main() {
) )
); );
// Wait for dividers to shrink.
await tester.pump(const Duration(milliseconds: 200));
boxes = tester.widgetList(find.byType(DecoratedBox)).toList(); boxes = tester.widgetList(find.byType(DecoratedBox)).toList();
offset = 3; offset = 3;
expect(isDivider(boxes[offset]), isTrue); expect(isDivider(boxes[offset], false, true), isTrue);
expect(isDivider(boxes[offset + 1]), isFalse); expect(isDivider(boxes[offset + 1], true, false), isTrue);
// offset + 2 is gap // offset + 2 is gap
expect(isDivider(boxes[offset + 3]), isTrue); expect(isDivider(boxes[offset + 3], false, true), isTrue);
expect(isDivider(boxes[offset + 4]), isFalse); expect(isDivider(boxes[offset + 4], true, false), isTrue);
}); });
} }
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