Skip to content

Instantly share code, notes, and snippets.

@orestesgaolin
Created August 25, 2019 12:24
Show Gist options
  • Save orestesgaolin/31917e8c6626baaa924bebe96fd6fd45 to your computer and use it in GitHub Desktop.
Save orestesgaolin/31917e8c6626baaa924bebe96fd6fd45 to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:math' as math;
class SliverGridPage extends StatefulWidget {
@override
_SliverGridPageState createState() => _SliverGridPageState();
}
class _SliverGridPageState extends State<SliverGridPage> {
ScrollController controller;
@override
void initState() {
super.initState();
controller = ScrollController();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
controller: controller,
slivers: <Widget>[
Test(controller: controller),
],
),
);
}
}
class Test extends StatelessWidget {
const Test({
Key key,
@required this.controller,
}) : super(key: key);
final ScrollController controller;
@override
Widget build(BuildContext context) {
return SliverAnimatedGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCountAndAnimation(
crossAxisCount: 2,
childAspectRatio: 1,
mainAxisSpacing: controller.offset,
controller: controller,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9) + 100],
child: Text(
'$index ',
style: TextStyle(
fontSize: 40,
color: Colors.white,
fontWeight: FontWeight.bold),
),
);
},
childCount: 100,
),
);
}
}
class SliverAnimatedGrid extends SliverMultiBoxAdaptorWidget {
const SliverAnimatedGrid({
Key key,
@required SliverChildDelegate delegate,
@required this.gridDelegate,
}) : super(key: key, delegate: delegate);
SliverAnimatedGrid.count({
Key key,
@required int crossAxisCount,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
double childAspectRatio = 1.0,
List<Widget> children = const <Widget>[],
}) : gridDelegate = SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
childAspectRatio: childAspectRatio,
),
super(key: key, delegate: SliverChildListDelegate(children));
SliverAnimatedGrid.extent({
Key key,
@required double maxCrossAxisExtent,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
double childAspectRatio = 1.0,
List<Widget> children = const <Widget>[],
}) : gridDelegate = SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: maxCrossAxisExtent,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
childAspectRatio: childAspectRatio,
),
super(key: key, delegate: SliverChildListDelegate(children));
final SliverGridDelegate gridDelegate;
@override
RenderSliverGrid createRenderObject(BuildContext context) {
final SliverMultiBoxAdaptorElement element = context;
return RenderSliverGrid(childManager: element, gridDelegate: gridDelegate);
}
@override
void updateRenderObject(BuildContext context, RenderSliverGrid renderObject) {
renderObject.gridDelegate = gridDelegate;
}
@override
double estimateMaxScrollOffset(
SliverConstraints constraints,
int firstIndex,
int lastIndex,
double leadingScrollOffset,
double trailingScrollOffset,
) {
return super.estimateMaxScrollOffset(
constraints,
firstIndex,
lastIndex,
leadingScrollOffset,
trailingScrollOffset,
) ??
gridDelegate
.getLayout(constraints)
.computeMaxScrollOffset(delegate.estimatedChildCount);
}
}
class SliverGridRegularTileLayout extends SliverGridLayout {
const SliverGridRegularTileLayout({
@required this.crossAxisCount,
@required this.mainAxisStride,
@required this.crossAxisStride,
@required this.childMainAxisExtent,
@required this.childCrossAxisExtent,
@required this.reverseCrossAxis,
}) : assert(crossAxisCount != null && crossAxisCount > 0),
assert(mainAxisStride != null && mainAxisStride >= 0),
assert(crossAxisStride != null && crossAxisStride >= 0),
assert(childMainAxisExtent != null && childMainAxisExtent >= 0),
assert(childCrossAxisExtent != null && childCrossAxisExtent >= 0),
assert(reverseCrossAxis != null);
final int crossAxisCount;
final double mainAxisStride;
final double crossAxisStride;
final double childMainAxisExtent;
final double childCrossAxisExtent;
final bool reverseCrossAxis;
@override
int getMinChildIndexForScrollOffset(double scrollOffset) {
return mainAxisStride > 0.0
? crossAxisCount * (scrollOffset ~/ mainAxisStride)
: 0;
}
@override
int getMaxChildIndexForScrollOffset(double scrollOffset) {
if (mainAxisStride > 0.0) {
final int mainAxisCount = (scrollOffset / mainAxisStride).ceil();
return math.max(0, crossAxisCount * mainAxisCount - 1);
}
return 0;
}
double _getOffsetFromStartInCrossAxis(double crossAxisStart) {
if (reverseCrossAxis)
return crossAxisCount * crossAxisStride -
crossAxisStart -
childCrossAxisExtent -
(crossAxisStride - childCrossAxisExtent);
return crossAxisStart;
}
@override
SliverGridGeometry getGeometryForChildIndex(int index) {
final double crossAxisStart = (index % crossAxisCount) * crossAxisStride;
return SliverGridGeometry(
scrollOffset: (index ~/ crossAxisCount) * mainAxisStride,
crossAxisOffset: _getOffsetFromStartInCrossAxis(crossAxisStart),
mainAxisExtent: childMainAxisExtent,
crossAxisExtent: childCrossAxisExtent,
);
}
@override
double computeMaxScrollOffset(int childCount) {
assert(childCount != null);
final int mainAxisCount = ((childCount - 1) ~/ crossAxisCount) + 1;
final double mainAxisSpacing = mainAxisStride - childMainAxisExtent;
return mainAxisStride * mainAxisCount - mainAxisSpacing;
}
}
class SliverGridDelegateWithFixedCrossAxisCountAndAnimation
extends SliverGridDelegate {
const SliverGridDelegateWithFixedCrossAxisCountAndAnimation({
@required this.crossAxisCount,
this.mainAxisSpacing = 0.0,
this.crossAxisSpacing = 0.0,
this.childAspectRatio = 1.0,
this.controller,
}) : assert(crossAxisCount != null && crossAxisCount > 0),
assert(mainAxisSpacing != null && mainAxisSpacing >= 0),
assert(crossAxisSpacing != null && crossAxisSpacing >= 0),
assert(childAspectRatio != null && childAspectRatio > 0);
/// Added scroll controller to have access to current position (velocity maybe?)
final ScrollController controller;
final int crossAxisCount;
final double mainAxisSpacing;
final double crossAxisSpacing;
final double childAspectRatio;
bool _debugAssertIsValid() {
assert(crossAxisCount > 0);
assert(mainAxisSpacing >= 0.0);
assert(crossAxisSpacing >= 0.0);
assert(childAspectRatio > 0.0);
return true;
}
@override
SliverGridLayout getLayout(SliverConstraints constraints) {
assert(_debugAssertIsValid());
final double usableCrossAxisExtent =
constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1);
final double childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
final double childMainAxisExtent = childCrossAxisExtent / childAspectRatio;
// Here accessing scroll controller velocity
final velocity = (controller.position.activity.velocity / 100.0).clamp(0, 20);
return SliverGridRegularTileLayout(
crossAxisCount: crossAxisCount,
mainAxisStride: childMainAxisExtent + mainAxisSpacing + velocity,
crossAxisStride: childCrossAxisExtent + crossAxisSpacing,
childMainAxisExtent: childMainAxisExtent,
childCrossAxisExtent: childCrossAxisExtent,
reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
);
}
@override
bool shouldRelayout(
SliverGridDelegateWithFixedCrossAxisCountAndAnimation oldDelegate) {
return oldDelegate.crossAxisCount != crossAxisCount ||
oldDelegate.mainAxisSpacing != mainAxisSpacing ||
oldDelegate.crossAxisSpacing != crossAxisSpacing ||
oldDelegate.childAspectRatio != childAspectRatio;
}
}
@orestesgaolin
Copy link
Author

excuse me. Did you solve it? How to animate items in list while scrolling in Flutter

unfortunately not

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment