Skip to content

Commit 432a9c7

Browse files
committed
Books added to fx widgets
1 parent cf7f68f commit 432a9c7

11 files changed

+291
-2
lines changed

assets/images/1_stoic.png

1.36 MB
Loading

assets/images/1_stoic_spine.png

158 KB
Loading

assets/images/2_moon.png

1.64 MB
Loading

assets/images/2_moon_spine.png

216 KB
Loading

assets/images/3_dog.png

1.32 MB
Loading

assets/images/3_dog_spine.png

201 KB
Loading

assets/images/4_bird.png

510 KB
Loading

assets/images/4_bird_spine.png

214 KB
Loading

lib/books/books.dart

+280
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
import 'dart:math';
2+
3+
import 'package:flutter/material.dart';
4+
5+
class BookShelfPage extends StatefulWidget {
6+
const BookShelfPage({super.key, required this.title});
7+
8+
final String title;
9+
10+
@override
11+
State<BookShelfPage> createState() => _BookShelfPageState();
12+
}
13+
14+
class _BookShelfPageState extends State<BookShelfPage> {
15+
//Dimension based on my book asset's aspect ratio. This makes sure the assets are not stretched.
16+
static const double fixedHeight = 300;
17+
static const double spineWidth = fixedHeight * 0.0957536;
18+
static const double coverWidth = fixedHeight * 0.65;
19+
20+
@override
21+
Widget build(BuildContext context) {
22+
final List<Book> books = [
23+
Book(
24+
coverAsset: 'assets/images/1_stoic.png',
25+
spineAsset: 'assets/images/1_stoic_spine.png',
26+
isOpen: false),
27+
Book(
28+
coverAsset: 'assets/images/2_moon.png',
29+
spineAsset: 'assets/images/2_moon_spine.png',
30+
isOpen: false),
31+
Book(
32+
coverAsset: 'assets/images/3_dog.png',
33+
spineAsset: 'assets/images/3_dog_spine.png',
34+
isOpen: false),
35+
Book(
36+
coverAsset: 'assets/images/4_bird.png',
37+
spineAsset: 'assets/images/4_bird_spine.png',
38+
isOpen: false),
39+
Book(
40+
coverAsset: 'assets/images/1_stoic.png',
41+
spineAsset: 'assets/images/1_stoic_spine.png',
42+
isOpen: false),
43+
Book(
44+
coverAsset: 'assets/images/2_moon.png',
45+
spineAsset: 'assets/images/2_moon_spine.png',
46+
isOpen: false),
47+
Book(
48+
coverAsset: 'assets/images/3_dog.png',
49+
spineAsset: 'assets/images/3_dog_spine.png',
50+
isOpen: false),
51+
Book(
52+
coverAsset: 'assets/images/4_bird.png',
53+
spineAsset: 'assets/images/4_bird_spine.png',
54+
isOpen: false),
55+
Book(
56+
coverAsset: 'assets/images/1_stoic.png',
57+
spineAsset: 'assets/images/1_stoic_spine.png',
58+
isOpen: false),
59+
Book(
60+
coverAsset: 'assets/images/2_moon.png',
61+
spineAsset: 'assets/images/2_moon_spine.png',
62+
isOpen: false),
63+
Book(
64+
coverAsset: 'assets/images/3_dog.png',
65+
spineAsset: 'assets/images/3_dog_spine.png',
66+
isOpen: false),
67+
Book(
68+
coverAsset: 'assets/images/4_bird.png',
69+
spineAsset: 'assets/images/4_bird_spine.png',
70+
isOpen: false),
71+
];
72+
return MaterialApp(
73+
debugShowCheckedModeBanner: false,
74+
home: Scaffold(
75+
body: Center(
76+
child: Container(
77+
height: 300, // Adjust as needed
78+
child: BookshelfWidget(books: books),
79+
),
80+
),
81+
),
82+
);
83+
}
84+
}
85+
86+
class BookWidget extends StatefulWidget {
87+
final Book book;
88+
final bool isOpen;
89+
90+
const BookWidget({
91+
Key? key,
92+
required this.book,
93+
required this.isOpen,
94+
}) : super(key: key);
95+
96+
@override
97+
_BookWidgetState createState() => _BookWidgetState();
98+
}
99+
100+
class _BookWidgetState extends State<BookWidget>
101+
with SingleTickerProviderStateMixin {
102+
late AnimationController _controller;
103+
late Animation<double> _animation;
104+
105+
@override
106+
void initState() {
107+
super.initState();
108+
_controller = AnimationController(
109+
duration: const Duration(milliseconds: 500),
110+
vsync: this,
111+
);
112+
_animation = Tween<double>(begin: -pi / 2, end: 0).animate(_controller);
113+
_updateAnimationState();
114+
}
115+
116+
@override
117+
void didUpdateWidget(BookWidget oldWidget) {
118+
super.didUpdateWidget(oldWidget);
119+
_updateAnimationState();
120+
}
121+
122+
void _updateAnimationState() {
123+
if (widget.isOpen) {
124+
_controller.forward();
125+
} else {
126+
_controller.reverse();
127+
}
128+
}
129+
130+
@override
131+
void dispose() {
132+
_controller.dispose();
133+
super.dispose();
134+
}
135+
136+
@override
137+
Widget build(BuildContext context) {
138+
return Transform.translate(
139+
offset: Offset(_BookShelfPageState.spineWidth, 0),
140+
child: AnimatedBuilder(
141+
animation: _animation,
142+
builder: (context, child) {
143+
return Transform(
144+
transform: Matrix4.identity()
145+
..setEntry(3, 2, 0.002)
146+
..rotateY(_animation.value)
147+
..translate(0.0, 0.0, 0.0),
148+
alignment: Alignment.centerLeft,
149+
child: _buildBookContent(),
150+
);
151+
},
152+
),
153+
);
154+
}
155+
156+
Widget _buildBookContent() {
157+
return Stack(
158+
children: [
159+
// Cover image
160+
Container(
161+
width: _BookShelfPageState.coverWidth,
162+
height: _BookShelfPageState.fixedHeight,
163+
child: Image.asset(
164+
widget.book.coverAsset,
165+
fit: BoxFit.fill,
166+
),
167+
),
168+
// Spine image
169+
Transform(
170+
transform: Matrix4.identity()
171+
..rotateY(pi / 2)
172+
..translate(-_BookShelfPageState.spineWidth, 0.0, 0.0),
173+
alignment: Alignment.centerLeft,
174+
child: Container(
175+
width: _BookShelfPageState.spineWidth,
176+
height: _BookShelfPageState.fixedHeight,
177+
child: Image.asset(
178+
widget.book.spineAsset,
179+
fit: BoxFit.fill,
180+
),
181+
),
182+
),
183+
],
184+
);
185+
}
186+
}
187+
188+
class Book {
189+
final String coverAsset;
190+
final String spineAsset;
191+
bool isOpen;
192+
193+
Book({
194+
required this.coverAsset,
195+
required this.spineAsset,
196+
this.isOpen = false,
197+
});
198+
}
199+
200+
class BookshelfWidget extends StatefulWidget {
201+
final List<Book> books;
202+
203+
const BookshelfWidget({Key? key, required this.books}) : super(key: key);
204+
205+
@override
206+
_BookshelfWidgetState createState() => _BookshelfWidgetState();
207+
}
208+
209+
class _BookshelfWidgetState extends State<BookshelfWidget> {
210+
late List<Book> _books;
211+
212+
@override
213+
void initState() {
214+
super.initState();
215+
_books = widget.books;
216+
}
217+
218+
void _toggleBook(int index) {
219+
print("on tap gesture");
220+
setState(() {
221+
for (int i = 0; i < _books.length; i++) {
222+
if (i == index) {
223+
_books[i].isOpen = !_books[i].isOpen;
224+
} else {
225+
_books[i].isOpen = false;
226+
}
227+
}
228+
});
229+
}
230+
231+
@override
232+
Widget build(BuildContext context) {
233+
return ListView.builder(
234+
padding: EdgeInsets.only(left: 20.0),
235+
clipBehavior: Clip.none,
236+
scrollDirection: Axis.horizontal,
237+
itemCount: widget.books.length,
238+
itemBuilder: (context, index) {
239+
return GestureDetector(
240+
onTap: () => _toggleBook(index),
241+
behavior: HitTestBehavior.opaque,
242+
child: Padding(
243+
padding: const EdgeInsets.symmetric(horizontal: 0.0),
244+
child: AnimatedBookWrapper(
245+
isOpen: _books[index].isOpen,
246+
child: BookWidget(
247+
book: _books[index],
248+
isOpen: _books[index].isOpen,
249+
),
250+
),
251+
),
252+
);
253+
},
254+
);
255+
}
256+
}
257+
258+
class AnimatedBookWrapper extends StatelessWidget {
259+
final Widget child;
260+
final bool isOpen;
261+
final Duration duration;
262+
263+
const AnimatedBookWrapper({
264+
Key? key,
265+
required this.child,
266+
required this.isOpen,
267+
this.duration = const Duration(milliseconds: 300),
268+
}) : super(key: key);
269+
270+
@override
271+
Widget build(BuildContext context) {
272+
return AnimatedContainer(
273+
duration: duration,
274+
width: isOpen
275+
? 240 //_MyHomePageState.coverWidth + 10
276+
: _BookShelfPageState.spineWidth + 5, // Adjust these values as needed
277+
child: child,
278+
);
279+
}
280+
}

lib/main.dart

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:flutter/material.dart';
2+
import 'package:fx_2_folder/books/books.dart';
23
import 'package:fx_2_folder/folder_shape/folder_home.dart';
34
import 'package:google_fonts/google_fonts.dart';
45

@@ -38,8 +39,8 @@ class HomeScreen extends StatelessWidget {
3839
),
3940
AnimationExample(
4041
title: 'Books',
41-
builder: (context) => const FolderHomeWidget(
42-
curve: Curves.easeInOutBack, title: 'EaseInOutBack'),
42+
builder: (context) =>
43+
const BookShelfPage(title: 'Flutter Demo Home Page'),
4344
),
4445
AnimationExample(
4546
title: 'Smoke',

pubspec.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ flutter:
6666
assets:
6767
- assets/images/folder_backcover.png
6868
- assets/images/folder_frontcover.png
69+
- assets/images/1_stoic.png
70+
- assets/images/1_stoic_spine.png
71+
- assets/images/2_moon.png
72+
- assets/images/2_moon_spine.png
73+
- assets/images/3_dog.png
74+
- assets/images/3_dog_spine.png
75+
- assets/images/4_bird.png
76+
- assets/images/4_bird_spine.png
6977

7078
# An image asset can refer to one or more resolution-specific "variants", see
7179
# https://flutter.dev/assets-and-images/#resolution-aware

0 commit comments

Comments
 (0)