Skip to content

Commit 93380a3

Browse files
committed
Responsive Grid Creation #1
*Create Responsive Grid widget.
1 parent f4aed68 commit 93380a3

File tree

2 files changed

+298
-0
lines changed

2 files changed

+298
-0
lines changed

lib/responsive_framework.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
library responsive_framework;
22

3+
export 'responsive_grid.dart';
34
export 'responsive_row_column.dart';
45
export 'responsive_value.dart';
56
export 'responsive_wrapper.dart';

lib/responsive_grid.dart

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
import 'package:flutter/gestures.dart';
2+
import 'package:flutter/rendering.dart';
3+
import 'package:flutter/widgets.dart';
4+
5+
class ResponsiveGridView extends StatelessWidget {
6+
final Axis scrollDirection;
7+
final bool reverse;
8+
final ScrollController controller;
9+
final bool primary;
10+
final ScrollPhysics physics;
11+
final bool shrinkWrap;
12+
final EdgeInsetsGeometry padding;
13+
final AlignmentGeometry alignment;
14+
final ResponsiveGridDelegate gridDelegate;
15+
final IndexedWidgetBuilder itemBuilder;
16+
final int itemCount;
17+
final int maxRowCount;
18+
final bool addAutomaticKeepAlives;
19+
final bool addRepaintBoundaries;
20+
final bool addSemanticIndexes;
21+
final double cacheExtent;
22+
final int semanticChildCount;
23+
final DragStartBehavior dragStartBehavior;
24+
final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
25+
26+
ResponsiveGridView.builder({
27+
Key key,
28+
this.scrollDirection = Axis.vertical,
29+
this.reverse = false,
30+
this.controller,
31+
this.primary,
32+
this.physics,
33+
this.shrinkWrap = false,
34+
this.padding,
35+
this.alignment = Alignment.centerLeft,
36+
@required this.gridDelegate,
37+
@required this.itemBuilder,
38+
this.itemCount,
39+
this.maxRowCount,
40+
this.addAutomaticKeepAlives = true,
41+
this.addRepaintBoundaries = true,
42+
this.addSemanticIndexes = true,
43+
this.cacheExtent,
44+
this.semanticChildCount,
45+
this.dragStartBehavior = DragStartBehavior.start,
46+
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
47+
}) : assert(gridDelegate != null);
48+
49+
@override
50+
Widget build(BuildContext context) {
51+
return LayoutBuilder(builder: (context, constraints) {
52+
int crossAxisCount;
53+
int maxItemCount;
54+
EdgeInsetsGeometry alignmentPadding;
55+
double crossAxisWidth;
56+
double crossAxisExtent = constraints.maxWidth - padding.horizontal;
57+
assert(crossAxisExtent > 0,
58+
'$padding exceeds layout width (${constraints.maxWidth})');
59+
if (gridDelegate.crossAxisExtent != null) {
60+
crossAxisCount = (crossAxisExtent /
61+
(gridDelegate.crossAxisExtent + gridDelegate.crossAxisSpacing))
62+
.floor();
63+
crossAxisWidth = crossAxisCount *
64+
(gridDelegate.crossAxisExtent + gridDelegate.crossAxisSpacing) +
65+
padding.horizontal;
66+
} else if (gridDelegate.maxCrossAxisExtent != null) {
67+
crossAxisCount = (crossAxisExtent /
68+
(gridDelegate.maxCrossAxisExtent +
69+
gridDelegate.crossAxisSpacing))
70+
.ceil();
71+
final double usableCrossAxisExtent = crossAxisExtent -
72+
gridDelegate.crossAxisSpacing * (crossAxisCount - 1);
73+
final double childCrossAxisExtent =
74+
usableCrossAxisExtent / crossAxisCount;
75+
crossAxisWidth = crossAxisCount *
76+
(childCrossAxisExtent + gridDelegate.crossAxisSpacing) +
77+
padding.horizontal;
78+
} else {
79+
crossAxisCount = (crossAxisExtent /
80+
(gridDelegate.minCrossAxisExtent +
81+
gridDelegate.crossAxisSpacing))
82+
.floor();
83+
final double usableCrossAxisExtent = crossAxisExtent -
84+
gridDelegate.crossAxisSpacing * (crossAxisCount - 1);
85+
final double childCrossAxisExtent =
86+
usableCrossAxisExtent / crossAxisCount;
87+
crossAxisWidth = crossAxisCount *
88+
(childCrossAxisExtent + gridDelegate.crossAxisSpacing) +
89+
padding.horizontal;
90+
}
91+
if (alignment == Alignment.centerLeft ||
92+
alignment == Alignment.topLeft ||
93+
alignment == Alignment.bottomLeft) {
94+
alignmentPadding = EdgeInsets.only(left: 0);
95+
} else if (alignment == Alignment.center ||
96+
alignment == Alignment.topCenter ||
97+
alignment == Alignment.bottomCenter) {
98+
double paddingCalc = constraints.maxWidth - crossAxisWidth;
99+
if (paddingCalc <= 0) {
100+
alignmentPadding = EdgeInsets.only(left: 0);
101+
} else if (paddingCalc > gridDelegate.crossAxisSpacing) {
102+
alignmentPadding = EdgeInsets.only(
103+
left: ((constraints.maxWidth -
104+
crossAxisWidth -
105+
gridDelegate.crossAxisSpacing) /
106+
2) +
107+
gridDelegate.crossAxisSpacing);
108+
} else {
109+
alignmentPadding = EdgeInsets.only(left: paddingCalc);
110+
}
111+
} else {
112+
alignmentPadding =
113+
EdgeInsets.only(left: constraints.maxWidth - crossAxisWidth);
114+
}
115+
if (maxRowCount != null) {
116+
maxItemCount = maxRowCount * crossAxisCount;
117+
}
118+
SliverChildDelegate childrenDelegate = SliverChildBuilderDelegate(
119+
itemBuilder,
120+
childCount: maxItemCount ?? itemCount,
121+
addAutomaticKeepAlives: addAutomaticKeepAlives,
122+
addRepaintBoundaries: addRepaintBoundaries,
123+
addSemanticIndexes: addSemanticIndexes);
124+
return Container(
125+
padding: alignmentPadding,
126+
child: _ResponsiveGridViewLayout(
127+
scrollDirection: scrollDirection,
128+
reverse: reverse,
129+
controller: controller,
130+
primary: primary,
131+
physics: physics,
132+
shrinkWrap: shrinkWrap,
133+
padding: padding,
134+
gridDelegate: gridDelegate,
135+
childrenDelegate: childrenDelegate,
136+
itemBuilder: itemBuilder,
137+
itemCount: itemCount,
138+
maxRowCount: maxRowCount,
139+
addAutomaticKeepAlives: addAutomaticKeepAlives,
140+
addRepaintBoundaries: addRepaintBoundaries,
141+
addSemanticIndexes: addSemanticIndexes,
142+
cacheExtent: cacheExtent,
143+
semanticChildCount: semanticChildCount,
144+
dragStartBehavior: dragStartBehavior,
145+
keyboardDismissBehavior: keyboardDismissBehavior,
146+
),
147+
);
148+
});
149+
}
150+
}
151+
152+
class _ResponsiveGridViewLayout extends BoxScrollView {
153+
final ResponsiveGridDelegate gridDelegate;
154+
final SliverChildDelegate childrenDelegate;
155+
final IndexedWidgetBuilder itemBuilder;
156+
final int itemCount;
157+
final int maxRowCount;
158+
final bool addAutomaticKeepAlives;
159+
final bool addRepaintBoundaries;
160+
final bool addSemanticIndexes;
161+
162+
_ResponsiveGridViewLayout({
163+
Key key,
164+
Axis scrollDirection = Axis.vertical,
165+
bool reverse = false,
166+
ScrollController controller,
167+
bool primary,
168+
ScrollPhysics physics,
169+
bool shrinkWrap = false,
170+
EdgeInsetsGeometry padding,
171+
@required this.gridDelegate,
172+
@required this.childrenDelegate,
173+
@required this.itemBuilder,
174+
this.itemCount,
175+
this.maxRowCount,
176+
this.addAutomaticKeepAlives = true,
177+
this.addRepaintBoundaries = true,
178+
this.addSemanticIndexes = true,
179+
double cacheExtent,
180+
int semanticChildCount,
181+
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
182+
ScrollViewKeyboardDismissBehavior keyboardDismissBehavior =
183+
ScrollViewKeyboardDismissBehavior.manual,
184+
}) : assert(gridDelegate != null),
185+
super(
186+
key: key,
187+
scrollDirection: scrollDirection,
188+
reverse: reverse,
189+
controller: controller,
190+
primary: primary,
191+
physics: physics,
192+
shrinkWrap: shrinkWrap,
193+
padding: padding,
194+
cacheExtent: cacheExtent,
195+
semanticChildCount: semanticChildCount ?? itemCount,
196+
dragStartBehavior: dragStartBehavior,
197+
keyboardDismissBehavior: keyboardDismissBehavior,
198+
);
199+
200+
@override
201+
Widget buildChildLayout(BuildContext context) {
202+
return SliverGrid(
203+
delegate: childrenDelegate,
204+
gridDelegate: gridDelegate,
205+
);
206+
}
207+
}
208+
209+
class ResponsiveGridDelegate extends SliverGridDelegate {
210+
const ResponsiveGridDelegate({
211+
this.crossAxisExtent,
212+
this.maxCrossAxisExtent,
213+
this.minCrossAxisExtent,
214+
this.mainAxisSpacing = 0,
215+
this.crossAxisSpacing = 0,
216+
this.childAspectRatio = 1,
217+
}) : assert(
218+
(crossAxisExtent != null && crossAxisExtent >= 0) ||
219+
(maxCrossAxisExtent != null && maxCrossAxisExtent >= 0) ||
220+
(minCrossAxisExtent != null && minCrossAxisExtent >= 0),
221+
'Must provide a valid cross axis extent.'),
222+
assert(mainAxisSpacing != null && mainAxisSpacing >= 0),
223+
assert(crossAxisSpacing != null && crossAxisSpacing >= 0),
224+
assert(childAspectRatio != null && childAspectRatio > 0);
225+
226+
final double crossAxisExtent;
227+
final double maxCrossAxisExtent;
228+
final double minCrossAxisExtent;
229+
final double mainAxisSpacing;
230+
final double crossAxisSpacing;
231+
final double childAspectRatio;
232+
233+
bool _debugAssertIsValid(double crossAxisExtent) {
234+
assert(crossAxisExtent > 0.0);
235+
assert(mainAxisSpacing >= 0.0);
236+
assert(crossAxisSpacing >= 0.0);
237+
assert(childAspectRatio > 0.0);
238+
return true;
239+
}
240+
241+
@override
242+
SliverGridLayout getLayout(SliverConstraints constraints) {
243+
assert(_debugAssertIsValid(constraints.crossAxisExtent));
244+
int crossAxisCount;
245+
double mainAxisStride;
246+
double crossAxisStride;
247+
double childMainAxisExtent;
248+
double childCrossAxisExtent;
249+
if (this.crossAxisExtent != null) {
250+
crossAxisCount =
251+
(constraints.crossAxisExtent / (crossAxisExtent + crossAxisSpacing))
252+
.floor();
253+
childCrossAxisExtent = crossAxisExtent;
254+
childMainAxisExtent = childCrossAxisExtent / childAspectRatio;
255+
mainAxisStride = childMainAxisExtent + mainAxisSpacing;
256+
crossAxisStride = childCrossAxisExtent + crossAxisSpacing;
257+
} else if (this.maxCrossAxisExtent != null) {
258+
crossAxisCount = (constraints.crossAxisExtent /
259+
(maxCrossAxisExtent + crossAxisSpacing))
260+
.ceil();
261+
final double usableCrossAxisExtent =
262+
constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1);
263+
childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
264+
childMainAxisExtent = childCrossAxisExtent / childAspectRatio;
265+
mainAxisStride = childMainAxisExtent + mainAxisSpacing;
266+
crossAxisStride = childCrossAxisExtent + crossAxisSpacing;
267+
} else {
268+
crossAxisCount = (constraints.crossAxisExtent /
269+
(minCrossAxisExtent + crossAxisSpacing))
270+
.floor();
271+
final double usableCrossAxisExtent =
272+
constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1);
273+
childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
274+
childMainAxisExtent = childCrossAxisExtent / childAspectRatio;
275+
mainAxisStride = childMainAxisExtent + mainAxisSpacing;
276+
crossAxisStride = childCrossAxisExtent + crossAxisSpacing;
277+
}
278+
return SliverGridRegularTileLayout(
279+
crossAxisCount: crossAxisCount,
280+
mainAxisStride: mainAxisStride,
281+
crossAxisStride: crossAxisStride,
282+
childMainAxisExtent: childMainAxisExtent,
283+
childCrossAxisExtent: childCrossAxisExtent,
284+
reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
285+
);
286+
}
287+
288+
@override
289+
bool shouldRelayout(ResponsiveGridDelegate oldDelegate) {
290+
return oldDelegate.crossAxisExtent != crossAxisExtent ||
291+
oldDelegate.maxCrossAxisExtent != maxCrossAxisExtent ||
292+
oldDelegate.minCrossAxisExtent != minCrossAxisExtent ||
293+
oldDelegate.mainAxisSpacing != mainAxisSpacing ||
294+
oldDelegate.crossAxisSpacing != crossAxisSpacing ||
295+
oldDelegate.childAspectRatio != childAspectRatio;
296+
}
297+
}

0 commit comments

Comments
 (0)