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.
diff --git a/packages/flutter/lib/src/material/mergeable_material.dart b/packages/flutter/lib/src/material/mergeable_material.dart
index 71ef31e..a66363b 100644
--- a/packages/flutter/lib/src/material/mergeable_material.dart
+++ b/packages/flutter/lib/src/material/mergeable_material.dart
@@ -82,6 +82,7 @@
Key key,
this.mainAxis: Axis.vertical,
this.elevation: 2,
+ this.hasDividers: false,
this.children: const <MergeableMaterialItem>[]
}) : super(key: key);
@@ -94,6 +95,9 @@
/// The elevation of all the [Material] slices.
final int elevation;
+ /// Whether connected pieces of [MaterialSlice] have dividers between them.
+ final bool hasDividers;
+
@override
String toString() {
return 'MergeableMaterial('
@@ -234,6 +238,21 @@
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
void didUpdateConfig(MergeableMaterial oldConfig) {
super.didUpdateConfig(oldConfig);
@@ -253,17 +272,7 @@
assert(_debugGapsAreValid(newChildren));
- while (j < _children.length) {
- if (_children[j] is MaterialGap &&
- _animationTuples[_children[j].key].controller.status
- == AnimationStatus.dismissed) {
- _removeChild(j);
- } else {
- j += 1;
- }
- }
-
- j = 0;
+ _removeEmptyGaps();
while (i < newChildren.length && j < _children.length) {
if (newOnly.contains(newChildren[i].key) ||
@@ -471,10 +480,37 @@
);
}
+ 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
Widget build(BuildContext context) {
+ _removeEmptyGaps();
+
final List<Widget> widgets = <Widget>[];
List<Widget> slices = <Widget>[];
+ List<Key> keys = <Key>[];
int i;
for (i = 0; i < _children.length; i += 1) {
@@ -489,11 +525,12 @@
),
child: new BlockBody(
mainAxis: config.mainAxis,
- children: slices
+ children: _divideSlices(context, slices, keys)
)
)
);
slices = <Widget>[];
+ keys = <Key>[];
widgets.add(
new SizedBox(
@@ -506,14 +543,11 @@
slices.add(
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,
child: slice.child
)
);
+ keys.add(new _MergeableMaterialSliceKey(_children[i].key));
}
}
@@ -527,11 +561,12 @@
),
child: new BlockBody(
mainAxis: config.mainAxis,
- children: slices
+ children: _divideSlices(context, slices, keys)
)
)
);
slices = <Widget>[];
+ keys = <Key>[];
}
return new _MergeableMaterialBlockBody(
@@ -543,6 +578,8 @@
}
}
+// The parent hierarchy can change and lead to the slice being
+// rebuilt. Usinga global key solves the issue.
class _MergeableMaterialSliceKey extends GlobalKey {
const _MergeableMaterialSliceKey(this.value) : super.constructor();
@@ -558,6 +595,11 @@
@override
int get hashCode => value.hashCode;
+
+ @override
+ String toString() {
+ return '_MergeableMaterialSliceKey($value)';
+ }
}
class _MergeableMaterialBlockBody extends BlockBody {
diff --git a/packages/flutter/test/material/mergeable_material_test.dart b/packages/flutter/test/material/mergeable_material_test.dart
index 9fd6c72..d031543 100644
--- a/packages/flutter/test/material/mergeable_material_test.dart
+++ b/packages/flutter/test/material/mergeable_material_test.dart
@@ -1014,4 +1014,116 @@
matches(getBorderRadius(tester, 0), 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);
+ });
}