Skip to content

Instantly share code, notes, and snippets.

@diegoveloper
Last active November 26, 2019 10:02
Show Gist options
  • Save diegoveloper/75e55ca2e4cee03bff41a26254d6fcf6 to your computer and use it in GitHub Desktop.
Save diegoveloper/75e55ca2e4cee03bff41a26254d6fcf6 to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart';
class PageMain extends StatefulWidget {
@override
_PageMainState createState() => _PageMainState();
}
class _PageMainState extends State<PageMain> {
CustomScrollController firstScroll = CustomScrollController();
CustomScrollController secondScrollController = CustomScrollController();
@override
void initState() {
super.initState();
firstScroll.addListener(() {
//THIS IS called when scroll is triggered,
secondScrollController.jumpToWithoutGoingIdleAndKeepingBallistic(
firstScroll.offset); // THIS will sync the scroll;
});
secondScrollController.addListener(() {
//THIS IS called when scroll is triggered,
firstScroll.jumpToWithoutGoingIdleAndKeepingBallistic(
secondScrollController.offset); // THIS will sync the scroll;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: 20,
controller: firstScroll,
itemExtent: 100,
itemBuilder: (context, index) => Container(
color: Colors.red,
child: Text("item: $index"),
margin: EdgeInsets.all(5.0),
),
),
),
Expanded(
child: ListView.builder(
controller: secondScrollController,
itemExtent: 100,
itemCount: 20,
itemBuilder: (context, index) => Container(
color: Colors.blue,
child: Text("item: $index"),
margin: EdgeInsets.all(5.0),
),
),
),
],
),
),
);
}
}
class CustomScrollController extends ScrollController {
CustomScrollController({
double initialScrollOffset = 0.0,
keepScrollOffset = true,
debugLabel,
}) : super(
initialScrollOffset: initialScrollOffset,
keepScrollOffset: keepScrollOffset,
debugLabel: debugLabel);
@override
_UnboundedScrollPosition createScrollPosition(
ScrollPhysics physics,
ScrollContext context,
ScrollPosition oldPosition,
) {
return _UnboundedScrollPosition(
physics: physics,
context: context,
oldPosition: oldPosition,
initialPixels: initialScrollOffset,
);
}
void jumpToWithoutGoingIdleAndKeepingBallistic(double value) {
assert(positions.isNotEmpty, 'ScrollController not attached.');
for (_UnboundedScrollPosition position
in new List<ScrollPosition>.from(positions))
position.jumpToWithoutGoingIdleAndKeepingBallistic(value);
}
}
class _UnboundedScrollPosition extends ScrollPositionWithSingleContext {
_UnboundedScrollPosition({
ScrollPhysics physics,
ScrollContext context,
ScrollPosition oldPosition,
double initialPixels,
}) : super(
physics: physics,
context: context,
oldPosition: oldPosition,
initialPixels: initialPixels,
);
/// There is a feedback-loop between aboveController and belowController. When one of them is
/// being used, it controls the other. However if they get out of sync, for timing reasons,
/// the controlled one with try to control the other, and the jump will stop the real controller.
/// For this reason, we can't let one stop the other (idle and ballistics) in this situation.
void jumpToWithoutGoingIdleAndKeepingBallistic(double value) {
if (pixels != value) {
forcePixels(value);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment