Skip to content

Commit 1f0f11f

Browse files
committed
added scrolling donuts example
1 parent ad46646 commit 1f0f11f

File tree

2 files changed

+211
-2
lines changed

2 files changed

+211
-2
lines changed
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import 'dart:developer';
2+
import 'dart:math' as math;
3+
import 'dart:ui';
4+
5+
import 'package:flutter/material.dart';
6+
7+
class ScrollingDonuts extends StatefulWidget {
8+
const ScrollingDonuts({super.key});
9+
10+
@override
11+
State<ScrollingDonuts> createState() => _ScrollingDonutsState();
12+
}
13+
14+
class _ScrollingDonutsState extends State<ScrollingDonuts> {
15+
@override
16+
Widget build(BuildContext context) {
17+
return Scaffold(
18+
body: Center(
19+
child: Stack(
20+
children: [
21+
Positioned.fill(
22+
child: CustomPaint(
23+
painter: DonutWheelPainter(),
24+
),
25+
),
26+
SizedBox(
27+
height: 500,
28+
child: ListCurvedView(
29+
childrens:
30+
// List.generate(12, (index) {
31+
// return AspectRatio(
32+
// aspectRatio: 1,
33+
// child: Image.asset(
34+
// 'assets/images/pokemons/${index + 1}.png',
35+
// ),
36+
// );
37+
// })
38+
39+
[
40+
SizedBox(
41+
height: 300,
42+
width: 300,
43+
child: Image.asset(
44+
'assets/images/donut_1.png',
45+
),
46+
),
47+
SizedBox(
48+
height: 300,
49+
width: 300,
50+
child: Image.asset(
51+
'assets/images/donut_2.png',
52+
),
53+
),
54+
SizedBox(
55+
height: 300,
56+
width: 300,
57+
child: Image.asset(
58+
'assets/images/donut_3.png',
59+
),
60+
),
61+
SizedBox(
62+
height: 300,
63+
width: 300,
64+
child: Image.asset(
65+
'assets/images/donut_1.png',
66+
),
67+
),
68+
SizedBox(
69+
height: 300,
70+
width: 300,
71+
child: Image.asset(
72+
'assets/images/donut_2.png',
73+
),
74+
),
75+
],
76+
),
77+
),
78+
],
79+
),
80+
),
81+
);
82+
}
83+
}
84+
85+
class ListCurvedView extends StatefulWidget {
86+
const ListCurvedView({
87+
Key? key,
88+
required this.childrens,
89+
}) : super(key: key);
90+
91+
final List<Widget> childrens;
92+
93+
@override
94+
State<ListCurvedView> createState() => _ListCurvedViewState();
95+
}
96+
97+
class _ListCurvedViewState extends State<ListCurvedView> {
98+
late final PageController pageController;
99+
static const double degree = math.pi / 180;
100+
101+
@override
102+
void initState() {
103+
// TODO: implement initState
104+
super.initState();
105+
pageController = PageController(
106+
viewportFraction: 0.3,
107+
initialPage: 2,
108+
);
109+
}
110+
111+
@override
112+
Widget build(BuildContext context) {
113+
return PageView.builder(
114+
controller: pageController,
115+
itemCount: widget.childrens.length,
116+
scrollDirection: Axis.horizontal,
117+
physics: const AlwaysScrollableScrollPhysics(),
118+
onPageChanged: (value) {},
119+
itemBuilder: ((context, index) {
120+
return AnimatedBuilder(
121+
animation: pageController,
122+
child: widget.childrens[index],
123+
builder: (context, child) {
124+
final position = pageController.position;
125+
late final double value;
126+
if (position.hasPixels && position.hasContentDimensions) {
127+
var page = pageController.page;
128+
if (page != null) {
129+
value = page - index;
130+
}
131+
} else {
132+
BuildContext storageContext = pageController.position.context.storageContext;
133+
final double? previousSavedPosition =
134+
PageStorage.of(storageContext).readState(storageContext) as double?;
135+
if (previousSavedPosition != null) {
136+
value = previousSavedPosition - index.toDouble();
137+
} else {
138+
value = pageController.initialPage - index.toDouble();
139+
140+
log(value.toString());
141+
}
142+
}
143+
final double distortionRatio = (1 - (value.abs() * 0.3)).clamp(0.0, 1.0);
144+
const offset = 0.5;
145+
final beforeLerp = lerpDouble(0, 0.5, value.abs())!;
146+
final scalingValue = beforeLerp + offset;
147+
148+
final lastValue = scalingValue >= 1.0 ? lerpDouble(1 + offset, 1, scalingValue) : scalingValue;
149+
final distortionValue = Curves.linear.transform(distortionRatio);
150+
if (index == 3) {
151+
log(value.toString());
152+
}
153+
154+
return Center(
155+
child: Opacity(
156+
opacity: distortionValue,
157+
child: Transform.translate(
158+
offset: Offset(
159+
// 0, 0,
160+
0,
161+
// -(value.abs() * 100).toDouble(),
162+
-(value * 100).toDouble(),
163+
),
164+
child: Transform.scale(
165+
scale: distortionValue,
166+
child: Transform.rotate(
167+
angle: value * 45 * degree * 10,
168+
child: child,
169+
),
170+
),
171+
),
172+
),
173+
);
174+
});
175+
}),
176+
);
177+
}
178+
}
179+
180+
class DonutWheelPainter extends CustomPainter {
181+
@override
182+
void paint(Canvas canvas, Size size) {
183+
final path = Path()
184+
..lineTo(0, size.height * 0.2)
185+
..quadraticBezierTo(
186+
size.width / 2,
187+
size.height * 0.35,
188+
size.width,
189+
size.height * 0.2,
190+
)
191+
..lineTo(size.width, size.height * 0.7)
192+
..quadraticBezierTo(
193+
size.width / 2,
194+
size.height * 0.8,
195+
0,
196+
size.height * 0.7,
197+
)
198+
..close();
199+
canvas.drawPath(
200+
path,
201+
Paint()..color = Colors.pink,
202+
);
203+
}
204+
205+
@override
206+
bool shouldRepaint(covariant CustomPainter oldDelegate) {
207+
return true;
208+
}
209+
}

lib/main.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import 'package:flutter/material.dart';
2-
import 'package:flutter_animations/flutter_design_challenges/power_up/power_up.dart';
2+
import 'package:flutter_animations/flutter_design_challenges/rolling_donuts/rolling_donuts.dart';
33

44
void main() {
55
runApp(const MyApp());
@@ -18,7 +18,7 @@ class MyApp extends StatelessWidget {
1818
primarySwatch: Colors.blue,
1919
// textTheme: GoogleFonts.rubikTextTheme(Theme.of(context).textTheme),
2020
),
21-
home: const PowerUp(),
21+
home: const ScrollingDonuts(),
2222
);
2323
}
2424
}

0 commit comments

Comments
 (0)