Skip to content

Instantly share code, notes, and snippets.

@rohan20
Created August 3, 2022 22:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rohan20/0a89c207c8daabb0477897235a532156 to your computer and use it in GitHub Desktop.
Save rohan20/0a89c207c8daabb0477897235a532156 to your computer and use it in GitHub Desktop.
LinkedScrollController Flutter example
const double _rankWidth = 50;
const double _teamInfoWidth = 90;
const double _ptsWidth = cellWidth; // cellWidth is 60 (from another file)
class _LeagueTable extends StatefulWidget {
final List<LeagueTableTeam> leagueTableTeams;
const _LeagueTable({required this.leagueTableTeams, Key? key}) : super(key: key);
@override
State<_LeagueTable> createState() => _LeagueTableState();
}
class _LeagueTableState extends State<_LeagueTable> {
static const int _numberOfTeams = 20;
late final LinkedScrollControllerGroup horizontalScrollControllersGroup;
late final LinkedScrollControllerGroup verticalScrollControllersGroup;
// horizontal scroll controllers
late final ScrollController statHeadersHorizontalScrollController;
late final List<ScrollController> statValuesHorizontalScrollControllers;
// vertical scroll controllers
late final ScrollController teamInfoVerticalScrollController;
late final ScrollController teamStatsVerticalScrollController;
@override
void initState() {
super.initState();
horizontalScrollControllersGroup = LinkedScrollControllerGroup();
statHeadersHorizontalScrollController = horizontalScrollControllersGroup.addAndGet();
statValuesHorizontalScrollControllers = [];
statValuesHorizontalScrollControllers.addAll(
List.generate(_numberOfTeams, (_) => horizontalScrollControllersGroup.addAndGet()),
);
verticalScrollControllersGroup = LinkedScrollControllerGroup();
teamInfoVerticalScrollController = verticalScrollControllersGroup.addAndGet();
teamStatsVerticalScrollController = verticalScrollControllersGroup.addAndGet();
}
@override
void dispose() {
statHeadersHorizontalScrollController.dispose();
for (final ScrollController controller in statValuesHorizontalScrollControllers) {
controller.dispose();
}
teamInfoVerticalScrollController.dispose();
teamStatsVerticalScrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
_ColumnHeaders(scrollController: statHeadersHorizontalScrollController),
Expanded(
child: Row(
children: [
// The fixed columns on the left
_TeamInfoColumns(
leagueTableTeams: widget.leagueTableTeams,
scrollController: teamInfoVerticalScrollController,
),
// The horizontally scrollable columns on the right
_StatValuesColumns(
leagueTableTeams: widget.leagueTableTeams,
scrollController: teamStatsVerticalScrollController,
statValuesHorizontalScrollControllers: statValuesHorizontalScrollControllers,
),
],
),
),
],
);
}
}
class _ColumnHeaders extends StatelessWidget {
final ScrollController scrollController;
const _ColumnHeaders({required this.scrollController});
@override
Widget build(BuildContext context) {
return Container(
height: cellHeight,
child: Row(
children: [
const FixedColumnHeaderCell("#", width: _rankWidth),
const FixedColumnHeaderCell("Team", width: _teamInfoWidth),
const FixedColumnHeaderCell("Pts", width: _ptsWidth),
Expanded(
child: ListView(
controller: scrollController,
physics: const ClampingScrollPhysics(),
scrollDirection: Axis.horizontal,
children: [
const ColumnHeaderCell("P"),
const ColumnHeaderCell("W"),
const ColumnHeaderCell("D"),
const ColumnHeaderCell("L"),
const ColumnHeaderCell("GF"),
const ColumnHeaderCell("GA"),
const ColumnHeaderCell("GD"),
],
),
),
],
),
);
}
}
class _TeamInfoColumns extends StatelessWidget {
final List<LeagueTableTeam> leagueTableTeams;
final ScrollController scrollController;
const _TeamInfoColumns({required this.leagueTableTeams, required this.scrollController});
@override
Widget build(BuildContext context) {
return Container(
width: _rankWidth + _teamInfoWidth + _ptsWidth,
child: ListView.builder(
controller: scrollController,
itemCount: leagueTableTeams.length,
itemBuilder: (_, index) {
final LeagueTableTeam leagueTableTeam = leagueTableTeams[index];
return Row(
children: [
ValueText("${index + 1}", width: _rankWidth, color: Colors.black54.withOpacity(0.1)), //rank
Container(
height: cellHeight,
width: _teamInfoWidth,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
decoration: BoxDecoration(border: Border.all(width: 1, color: ColorsDesignToken.neutral)),
child: Row(
children: [
Expanded(flex: 1, child: TeamImage(teamId: leagueTableTeam.id)),
Expanded(flex: 2, child: CoreText.titleSm(leagueTableTeam.id.teamShortName, maxLines: 1)),
],
),
),
ValueText(
leagueTableTeam.points.toString(),
color: ColorsDesignToken.backgroundPrimary.withOpacity(0.1),
width: _ptsWidth,
),
],
);
},
),
);
}
}
class _StatValuesColumns extends StatelessWidget {
final List<LeagueTableTeam> leagueTableTeams;
final ScrollController scrollController;
final List<ScrollController> statValuesHorizontalScrollControllers;
const _StatValuesColumns({
required this.leagueTableTeams,
required this.scrollController,
required this.statValuesHorizontalScrollControllers,
});
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
controller: scrollController,
itemCount: leagueTableTeams.length,
itemBuilder: (_, index) {
final LeagueTableTeam leagueTableTeam = leagueTableTeams[index];
return SingleChildScrollView(
physics: const ClampingScrollPhysics(),
controller: statValuesHorizontalScrollControllers[index],
scrollDirection: Axis.horizontal,
child: Row(
children: [
ValueText(leagueTableTeam.played.toString()),
ValueText(leagueTableTeam.wins.toString()),
ValueText(leagueTableTeam.draws.toString()),
ValueText(leagueTableTeam.loses.toString()),
ValueText(leagueTableTeam.goalsScored.toString()),
ValueText(leagueTableTeam.goalsConceded.toString()),
ValueText(leagueTableTeam.goalDifference.toString()),
],
),
);
},
),
);
}
}
@rohan20
Copy link
Author

rohan20 commented Aug 3, 2022

linked-scroll-controller.mp4

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