A professional, high-fidelity 3D-like page flip engine for Flutter. Specifically engineered to deliver ultra-smooth 60/120 FPS performance even on low-end devices through advanced rendering optimizations.
English | ํ๊ตญ์ด
Most page flip libraries struggle with performance as UI complexity increases. Real Page Flip is built differently:
Unlike other libraries that attempt to render live widget trees during heavy animations, our engine captures high-resolution snapshots of your pages.
- The Benefit: During a flip, the GPU only handles a single flattened texture (RawImage) instead of hundreds of nested widgets. This guarantees silky-smooth motion even with extremely complex page layouts.
Whether your book has 10 pages or 10,000, the memory footprint remains constant.
- The Benefit: We only maintain the current, previous, and next pages in the widget tree. This prevents the "Memory Bloat" common in standard PageView-based implementations.
We avoid heavy 3D perspective transforms that can be jittery on older hardware. Instead, we use a custom math-based Path Clipping engine.
- The Benefit: Perfectly clean curls, dynamic shadows, and specular highlights with minimal computational overhead.
Ever had a "Vertical viewport was given unbounded height" error? Not here.
- The Benefit: Our internal "Constraint Gate" ensures the engine works perfectly inside any parentโbe it a Stack, Column, or Scaffoldโwithout manual size adjustments.
What truly sets this engine apart is the immersive sensory feedback:
- Physical Sound Effects: High-quality rustle sounds that vary naturally with your gesture speed.
- Tactile Haptics: Feel the friction and the "snap" of the paper through your device's haptic engine.
Add real_page_flip to your pubspec.yaml:
dependencies:
real_page_flip: ^1.4.0import 'package:real_page_flip/real_page_flip.dart';
PageFlipWidget(
itemCount: 10,
itemBuilder: (context, index) => MyPage(index),
)Control the drag-release threshold for completing page flips in each direction:
PageFlipWidget(
config: PageFlipConfig(
// Forward flip completes when drag exceeds 40 % (default 0.4)
cutoffForward: 0.35,
// Backward flip threshold (default 0.4)
cutoffPrevious: 0.5,
// Overall gesture sensitivity (0.0 = firm, 1.0 = light touch)
sensitivity: 0.5,
),
itemCount: 10,
itemBuilder: (context, index) => MyPage(index),
)Higher values require dragging further across the page to complete a flip. Setting forward/previous independently lets you tune bias (e.g. easier to go forward than backward).
For books that show left and right pages together:
PageFlipWidget(
spreadMode: PageFlipSpreadMode.doubleSpread, // or isDoubleSpread: true
itemCount: spreadCount, // number of spreads, not single pages
config: PageFlipConfig(
skipTapAnimation: false, // required to animate spine-band reveal on tap
),
itemBuilder: (context, spreadIndex) => MyTwoPageSpread(spreadIndex),
)Host contract
| Responsibility | Detail |
|---|---|
itemBuilder |
Each index renders a full-width spread (left + right pages). |
itemCount |
Number of spreads (e.g. ceil(pageCount / 2)). |
| Stable builder | Use a method or const closureโnot a new inline lambda every build, or snapshots reset too often. |
| Snapshots | The engine captures spreadSnapshots[currentIndex ยฑ 1] when includeCurrentSpread is true (flip start, page settle, init). |
| Spine reveal | Forward flip reveals the left half of the next spread; backward reveals the right half of the previous spread. |
Use clipSpreadPageHalf from the engine when aligning host layout with flip layers.
The engine is theme-aware by default. With backgroundColor: null (the
default since v1.3.0), the flipping page automatically uses the host app's
scaffoldBackgroundColor, and shadow/highlight intensities are calculated from
the background luminance at paint time.
MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.system,
home: Scaffold(
body: PageFlipWidget(
itemCount: pages.length,
itemBuilder: (context, index) => MyPage(index),
// No config needed โ dark mode just works
),
),
)final isDark = Theme.of(context).brightness == Brightness.dark;
PageFlipWidget(
config: PageFlipConfig(
backgroundColor: isDark
? const Color(0xFF1A1F3A) // dark navy
: const Color(0xFFEEEEEE), // warm paper
),
itemCount: pages.length,
itemBuilder: (context, index) => MyPage(index),
)| Element | Light mode | Dark mode |
|---|---|---|
| Paper back color | scaffoldBackgroundColor |
scaffoldBackgroundColor |
| Inner shadow strength | 35 % | 20 % (softer) |
| Fold highlight | 5 % | 18 % (stronger for depth) |
| Edge tap indicator | Dark glow | Light glow |
Note: Page content (text, images, backgrounds) is controlled by your
itemBuilder. The engine only manages the flip animation layer.
This project uses a Dual License model:
- Non-commercial: Free for personal/open-source projects.
- Commercial: Requires a paid license for revenue-generating products. See LICENSE for details.
Real Page Flip Engine์ ํ๋ฌํฐ๋ฅผ ์ํ ๊ณ ์ฑ๋ฅ ๋ฌผ๋ฆฌ ๊ธฐ๋ฐ ํ์ด์ง ์ ํ ์์ง์ ๋๋ค. ํนํ ์ ์ฌ์ ๊ธฐ๊ธฐ์์๋ ๋๊น ์๋ 60/120 FPS ์ฑ๋ฅ์ ๋ณด์ฅํ๊ธฐ ์ํด ์ค๊ณ๋ ๋ ๋ณด์ ์ธ ๋ ๋๋ง ์ต์ ํ ๊ธฐ์ ์ด ์ ์ฉ๋์์ต๋๋ค.
- ํ์ด๋ธ๋ฆฌ๋ ์ค๋ ์ท ์์ง: ์ ๋๋ฉ์ด์ ์ค ๋ณต์กํ ์์ ฏ ํธ๋ฆฌ๋ฅผ ๋งค ํ๋ ์ ๋ค์ ๊ทธ๋ฆฌ๋ ๋์ , ํ์ด์ง๋ฅผ ๊ณ ํด์๋ ์ด๋ฏธ์ง๋ก ์บก์ฒํ์ฌ ์ฒ๋ฆฌํฉ๋๋ค. ๋๋ถ์ ์๋ฌด๋ฆฌ ๋ณต์กํ UI๋ผ๋ GPU ๋ถํ ์์ด ๋ถ๋๋ฝ๊ฒ ๋์ด๊ฐ๋๋ค.
- ์ง๋ฅํ ๋ฉ๋ชจ๋ฆฌ ์๋์: ์๋ง ์ฅ์ ํ์ด์ง๊ฐ ์์ด๋ ํ์ฌ์ ์๋ค ํ์ด์ง, ๋จ 3์ฅ๋ง ๋ฉ๋ชจ๋ฆฌ์ ์ ์งํ์ฌ ๋ฆฌ์์ค ๋ญ๋น๋ฅผ ์์ฒ ์ฐจ๋จํฉ๋๋ค.
- ์ ๋ก-์ค๋ฒํค๋ ์ง์ค๋ฉํธ๋ฆฌ: ๋ฌด๊ฑฐ์ด 3D ๋ณํ ๋์ ์ ๊ตํ ์ํ์ ๊ฒฝ๋ก ํด๋ฆฌํ(Path Clipping)์ ์ฌ์ฉํ์ฌ ๊นจ๋ํ ์ข ์ด ํ์ด์ง๊ณผ ๊ทธ๋ฆผ์ ํจ๊ณผ๋ฅผ ๊ตฌํํ์ต๋๋ค.
- ๊ฒฌ๊ณ ํ ๋ ์ด์์ ์ค๊ณ: 'Constraint Gate' ๊ตฌ์กฐ๋ฅผ ํตํด ์ด๋ค ๋ณต์กํ ์์ ฏ ํธ๋ฆฌ ์์์๋ ๋ ์ด์์ ์๋ฌ ์์ด ์์ ์ ์ผ๋ก ์๋ํฉ๋๋ค.
Built with by ChaPDCha

