Created
September 25, 2020 11:47
Star
You must be signed in to star a gist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
// This widget is the root of your application. | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
visualDensity: VisualDensity.adaptivePlatformDensity, | |
), | |
home: ListSnapPage(), | |
); | |
} | |
} | |
class ListSnapPage extends StatefulWidget { | |
@override | |
_ListSnapPageState createState() => _ListSnapPageState(); | |
} | |
class _ListSnapPageState extends State<ListSnapPage> { | |
// create a list of 6 items | |
List<String> articles = List.generate(6, (index) => "Article ${index + 1}"); | |
// for listening scroll position | |
PageController pageController; | |
// visible item position | |
int currentPage = 0; | |
// substraction value for seeing a little bit next item (right) | |
int itemOffset = 50; | |
// 'pageController.page' is not available immediatly. Access will cause a crash | |
// if it is true, we'll access to 'pageController.page' | |
bool isPageControllerLoaded = false; | |
AnimationController animationController; | |
int itemsLenght; | |
@override | |
void initState() { | |
super.initState(); | |
// store number of item | |
itemsLenght = articles.length; | |
// initialization with current page | |
pageController = PageController( | |
initialPage: currentPage, | |
); | |
pageController.addListener(() { | |
// if this block work, we'll update it value to true an access to 'pageController.page' | |
if (!isPageControllerLoaded) isPageControllerLoaded = true; | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
double screenWidth = MediaQuery.of(context).size.width; | |
return Scaffold( | |
body: SafeArea( | |
child: Column( | |
children: [ | |
Container( | |
height: 200, | |
child: ListView.builder( | |
// do not define 'itemCount' | |
scrollDirection: Axis.horizontal, | |
physics: PageScrollPhysics(), | |
controller: pageController, | |
itemBuilder: (context, index) { | |
return AnimatedBuilder( | |
animation: pageController, | |
child: _buildItem(index % (itemsLenght ~/ 2)), | |
builder: (context, child) { | |
var pageValue = | |
isPageControllerLoaded ? pageController.page : 0; | |
var value = (pageValue - index).clamp(0, 1); | |
// After some observations I noticed than from the second item | |
// 'value' of the previous item goes to 1.0 | |
// 'value' of the current and next items goes to 0.0 | |
print('index = $index value $value'); | |
// 'offset' should be equal to 'itemOffset' at the end of list scrolling | |
var offset = itemOffset * (1 - value); | |
return Container( | |
height: 200, | |
width: screenWidth - offset, | |
padding: EdgeInsets.all(4.0), | |
child: child, | |
); | |
}, | |
); | |
}, | |
), | |
) | |
], | |
), | |
), | |
); | |
} | |
// list item widget | |
Widget _buildItem(index) { | |
return Column( | |
children: [ | |
Column( | |
children: [ | |
Row( | |
children: [ | |
Placeholder( | |
fallbackWidth: 30, | |
fallbackHeight: 30, | |
), | |
SizedBox( | |
width: 10, | |
), | |
Expanded( | |
child: Text(articles[index % itemsLenght * 2]), | |
), | |
], | |
), | |
SizedBox( | |
height: 10, | |
), | |
Divider( | |
height: 1, | |
color: Colors.black, | |
) | |
], | |
), | |
SizedBox( | |
height: 10, | |
), | |
Column( | |
children: [ | |
Row( | |
children: [ | |
Placeholder( | |
fallbackWidth: 30, | |
fallbackHeight: 30, | |
), | |
SizedBox( | |
width: 10, | |
), | |
Expanded( | |
child: Text(articles[index % itemsLenght * 2 + 1]), | |
), | |
], | |
), | |
SizedBox( | |
height: 10, | |
), | |
Divider( | |
height: 1, | |
color: Colors.black, | |
) | |
], | |
), | |
], | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment