Reputation: 1314
I have a stack of positioned containers like this:
I would like to scroll them horizontally and bring that container to the front accordingly namely bring blue one to the front when scrolled right and yellow one when scrolled left.
Something like this but a scroll instead of clicks:
I tried using SingleChildScrollView with horizontal scroll but unsurprisingly it didn't work. The code I tried was:
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Stack(
clipBehavior: Clip.none,
children: [
Container(),
Positioned(
top: 145.h,
left: 100.w,
height: 300.h,
width: 250.w,
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
elevation: 3.h,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(5.r), //// <-- Radius
),
),
child: Text("pp"),
),
),
Positioned(
top: 145.h,
left: 20.w,
height: 300.h,
width: 250.w,
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.yellow,
elevation: 3.h,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(5.r), //// <-- Radius
),
),
child: Text("Demo 3"),
),
),
Positioned(
top: 145.h,
left: 60.w,
height: 300.h,
width: 250.w,
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
elevation: 3.h,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(5.r), //// <-- Radius
),
),
child: Text(
"Demo 2",
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.w500,
),
),
),
),
Positioned(
top: 145.h,
left: 60.w,
height: 300.h,
width: 250.w,
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
elevation: 3.h,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(5.r), //// <-- Radius
),
),
child: Text(
"Demo",
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
One solution I do know is to use InkWell and use onTap/ElevatedButton onPress to tweak the positioning and bring the corresponding container to the front but the user experience won't be as good as a horizontal scroll.
I also tried using ListWheelScrollView with RotatedBox to simulate horizontal scrolling but it was not good enough.
Any help on this would be greatly appreciated!
Upvotes: 2
Views: 1185
Reputation: 4556
First, take a look at the HTML and CSS code (the result looks like what you want):
HTML code:
<section id="slider">
<input type="radio" name="slider" id="s1">
<input type="radio" name="slider" id="s2">
<input type="radio" name="slider" id="s3" checked>
<input type="radio" name="slider" id="s4">
<input type="radio" name="slider" id="s5">
<label for="s1" id="slide1"></label>
<label for="s2" id="slide2"></label>
<label for="s3" id="slide3"></label>
<label for="s4" id="slide4"></label>
<label for="s5" id="slide5"></label>
</section>
CSS code:
* {
margin: 0;
padding: 0;
}
body {
padding: 20px;
background: #eee;
user-select: none;
}
[type=radio] {
display: none;
}
#slider {
height: 35vw;
position: relative;
perspective: 1000px;
transform-style: preserve-3d;
}
#slider label {
margin: auto;
width: 60%;
height: 100%;
border-radius: 4px;
position: absolute;
left: 0; right: 0;
cursor: pointer;
transition: transform 0.4s ease;
}
#s1:checked ~ #slide4, #s2:checked ~ #slide5,
#s3:checked ~ #slide1, #s4:checked ~ #slide2,
#s5:checked ~ #slide3 {
box-shadow: 0 1px 4px 0 rgba(0,0,0,.37);
transform: translate3d(-30%,0,-200px);
}
#s1:checked ~ #slide5, #s2:checked ~ #slide1,
#s3:checked ~ #slide2, #s4:checked ~ #slide3,
#s5:checked ~ #slide4 {
box-shadow: 0 6px 10px 0 rgba(0,0,0,.3), 0 2px 2px 0 rgba(0,0,0,.2);
transform: translate3d(-15%,0,-100px);
}
#s1:checked ~ #slide1, #s2:checked ~ #slide2,
#s3:checked ~ #slide3, #s4:checked ~ #slide4,
#s5:checked ~ #slide5 {
box-shadow: 0 13px 25px 0 rgba(0,0,0,.3), 0 11px 7px 0 rgba(0,0,0,.19);
transform: translate3d(0,0,0);
}
#s1:checked ~ #slide2, #s2:checked ~ #slide3,
#s3:checked ~ #slide4, #s4:checked ~ #slide5,
#s5:checked ~ #slide1 {
box-shadow: 0 6px 10px 0 rgba(0,0,0,.3), 0 2px 2px 0 rgba(0,0,0,.2);
transform: translate3d(15%,0,-100px);
}
#s1:checked ~ #slide3, #s2:checked ~ #slide4,
#s3:checked ~ #slide5, #s4:checked ~ #slide1,
#s5:checked ~ #slide2 {
box-shadow: 0 1px 4px 0 rgba(0,0,0,.37);
transform: translate3d(30%,0,-200px);
}
#slide1 { background: #00BCD4 }
#slide2 { background: #4CAF50 }
#slide3 { background: #CDDC39 }
#slide4 { background: #FFC107 }
#slide5 { background: #FF5722 }
Then, load the HTML and CSS code in Flutter. Try watching this YouTube video to load HTML and CSS code.
YouTube video source code: https://github.com/umairshah143/qrcode/tree/master/qrcodeapp/qrcodeapp
HTML and CSS source code: https://codepen.io/panfilov/pen/GogJVy
Upvotes: -2
Reputation: 1727
Well I found a solution with PageView.builder not with Stack
, which is pretty close to what you are trying to make,
home page where we call our carousel:
import 'dart:math' as math;
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
List items = List.generate(
20,
(index) => Color((math.Random().nextDouble() * 0xFFFFFF).toInt())
.withOpacity(1.0)); // list for test
late PageController _pageController; // controller of pageview
@override
void initState() {
_pageController = PageController(initialPage: 0, viewportFraction: 0.8);
super.initState();
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ScrollConfiguration(
behavior: CustomScrollBehavior(),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: CardCarousel(pageController: _pageController, items: items),
),
);
}
}
this is Carousel:
class CardCarousel extends StatelessWidget {
final PageController pageController;
final List items;
const CardCarousel(
{super.key, required this.pageController, required this.items});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 400.0,
width: 300,
child: PageView.builder(
controller: pageController,
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return _cardBuilder(context, index);
},
),
),
],
);
}
_cardBuilder(BuildContext context, int index) {
var item = items[index];
return AnimatedBuilder(
animation: pageController,
builder: (context, child) {
double value = 1;
if (pageController.position.haveDimensions) {
value = (pageController.page! - index);
value = (1 - (value.abs() * 0.25)).clamp(0.0, 1.0);
// you can change these values to change the size
// transformation that i found it
}
return Center(
child: Container(
color: item,
// change animation right here
// the value for transform have got above
height: Curves.easeInOut.transform(value) * 400.0,
child: child,
),
);
},
);
}
}
I hope this was helpful, the result be like:
Upvotes: 1
Reputation: 1998
Check out the CarouselSlider package CarouselSlider package this package is exactly what you are looking for. there is a youtube tutorial for it too. youtube tutorial
Upvotes: 0