turnable_page 0.0.1
turnable_page: ^0.0.1 copied to clipboard
Realistic page-flipping widget for Flutter with smooth animations and responsive layout.
Turnable Page #
A Flutter package that provides a realistic page-flipping effect for digital books, magazines, catalogs, and other multi-page content in Flutter applications.
Features #
✅ Realistic Physics: Advanced flip animations with proper physics and shadows
✅ Touch Support: Full touch and gesture support for mobile devices
✅ Multiple Orientations: Automatic portrait/landscape orientation handling
✅ Widget Support: Use any Flutter widget as page content
✅ Customizable: Extensive configuration options
✅ Performance: Hardware-accelerated rendering for smooth 60fps animations
✅ Events: Rich event system for interaction handling
✅ Responsive: Auto-sizing and responsive layout support
✅ Cross-Platform: Supports Mobile, Web, and Windows
Note: Widgets inside book pages are currently non-interactive (taps/gestures inside a page are not forwarded to child widgets).
Demo (GIF) #
Animated GIF previews below. On pub.dev, demo assets under videos/ are excluded to reduce package size; view them on GitHub.
Desktop flipping #
[Desktop flipping]
Mobile flipping #
[Mobile flipping]
Responsiveness #
[Responsiveness]
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
turnable_page: ^0.0.1
Then run:
flutter pub get
Basic Usage #
Simple Widget-Based Book #
import 'package:flutter/material.dart';
import 'package:turnable_page/turnable_page.dart';
class MyBook extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: 400,
height: 600,
child: TurnablePage(
pageCount: 6,
pageBuilder: (index, constraints) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.grey),
),
child: Center(
child: Text(
'Page ${index + 1}',
style: TextStyle(fontSize: 24),
),
),
);
},
),
),
),
);
}
}
With Controller and Events #
class BookWithController extends StatefulWidget {
@override
_BookWithControllerState createState() => _BookWithControllerState();
}
class _BookWithControllerState extends State<BookWithController> {
late PageFlipController _controller;
int _currentPage = 0;
@override
void initState() {
super.initState();
_controller = PageFlipController();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page ${_currentPage + 1}'),
actions: [
IconButton(
icon: Icon(Icons.arrow_back),
onPressed: _controller.hasPreviousPage
? () => _controller.previousPage()
: null,
),
IconButton(
icon: Icon(Icons.arrow_forward),
onPressed: _controller.hasNextPage
? () => _controller.nextPage()
: null,
),
],
),
body: TurnablePage(
controller: _controller,
pageCount: 10,
onPageChanged: (leftIndex, rightIndex) {
setState(() {
_currentPage = leftIndex;
});
},
pageBuilder: (index, constraints) {
return Container(
color: Colors.primaries[index % Colors.primaries.length],
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Page ${index + 1}',
style: TextStyle(fontSize: 32, color: Colors.white),
),
SizedBox(height: 20),
Icon(
Icons.book,
size: 64,
color: Colors.white,
),
],
),
),
);
},
),
);
}
}
Advanced Configuration #
TurnablePage(
pageCount: 20,
pageBuilder: pageBuilder,
controller: controller,
onPageChanged: onPageChanged,
// Visual appearance
pageViewMode: PageViewMode.single, // or PageViewMode.double
pixelRatio: 3.0,
aspectRatio: 2/3, // Custom aspect ratio
autoResponseSize: true,
paperBoundaryDecoration: PaperBoundaryDecoration.vintage,
// Flip settings
settings: FlipSettings(
// Page positioning
startPageIndex: 0,
// Size configuration
size: SizeType.fixed, // or SizeType.stretch
width: 400.0,
height: 600.0,
// Visual effects
drawShadow: true,
maxShadowOpacity: 1.0,
showPageCorners: true,
// Animation
flippingTime: 700, // milliseconds
// Behavior
usePortrait: true,
showCover: false,
mobileScrollSupport: true,
clickEventForward: false,
swipeDistance: 100.0,
disableFlipByClick: false,
),
)
API Reference #
TurnablePage Widget #
The main widget for creating a page-flipping book interface.
Constructor
TurnablePage({
Key? key,
PageFlipController? controller,
double? aspectRatio,
required TurnableBuilder pageBuilder,
required int pageCount,
TurnablePageCallback? onPageChanged,
PageViewMode pageViewMode = PageViewMode.single,
double pixelRatio = 3.0,
bool autoResponseSize = true,
PaperBoundaryDecoration paperBoundaryDecoration = PaperBoundaryDecoration.vintage,
FlipSettings? settings,
})
Parameters
controller- Optional controller for programmatic page controlpageBuilder- Builder function that creates widget content for each pagepageCount- Total number of pages in the bookonPageChanged- Callback fired when page changespageViewMode- Display mode: single page or double page spreadpixelRatio- Rendering pixel ratio for qualityautoResponseSize- Whether to automatically adjust size to containeraspectRatio- Custom aspect ratio for the bookpaperBoundaryDecoration- Visual style for page boundariessettings- Detailed flip behavior configuration
PageFlipController #
Controller class for programmatic page manipulation.
Methods
nextPage()- Turn to the next page (without animation)previousPage()- Turn to the previous page (without animation)goToPage(int pageIndex)- Jump to a specific page (without animation)flipNext([FlipCorner corner])- Flip to next page with animationflipPrev([FlipCorner corner])- Flip to previous page with animationflipToPage(int pageIndex, [FlipCorner corner])- Flip to specific page with animation
Properties
currentPageIndex- Get current page index (0-based)pageCount- Get total number of pageshasNextPage- Check if next page is availablehasPreviousPage- Check if previous page is availablecanFlipNext- Check if can flip to next pagecanFlipPrev- Check if can flip to previous page
FlipSettings Configuration #
Configuration object for customizing flip behavior and appearance.
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
startPageIndex |
int |
0 |
Initial page to display (0-based index) |
size |
SizeType |
SizeType.fixed |
Size calculation: fixed dimensions or stretch to fit |
width |
double |
0 |
Width of the book in pixels |
height |
double |
0 |
Height of the book in pixels |
drawShadow |
bool |
true |
Whether to draw realistic shadow effects |
flippingTime |
int |
700 |
Duration of flip animation in milliseconds |
usePortrait |
bool |
true |
Portrait mode (single page) vs landscape (two-page spread) |
maxShadowOpacity |
double |
1.0 |
Maximum opacity for shadow effects (0.0 to 1.0) |
showCover |
bool |
false |
Whether the book has a front/back cover |
mobileScrollSupport |
bool |
true |
Enable touch scrolling on mobile devices |
clickEventForward |
bool |
false |
Whether click events propagate to parent widgets |
swipeDistance |
double |
100.0 |
Minimum distance in pixels for swipe gesture |
showPageCorners |
bool |
true |
Show interactive corner highlighting on hover |
disableFlipByClick |
bool |
false |
Disable page flipping via click (drag only) |
PageViewMode
PageViewMode.single- Single page view (portrait orientation)PageViewMode.double- Double page spread (landscape orientation)
SizeType
SizeType.fixed- Fixed dimensions specified by width/heightSizeType.stretch- Stretch to fit parent container
FlipCorner
FlipCorner.topLeft- Flip from top-left cornerFlipCorner.topRight- Flip from top-right cornerFlipCorner.bottomLeft- Flip from bottom-left cornerFlipCorner.bottomRight- Flip from bottom-right corner
PaperBoundaryDecoration
PaperBoundaryDecoration.vintage- Vintage paper stylingPaperBoundaryDecoration.modern- Modern clean stylingPaperBoundaryDecoration.parchment- Parchment-style textured paper with warm, aged tones
Examples #
Note: Image-based examples inside the book have been temporarily removed and will be added in a future update.
Basic Book Reader with Navigation #
class BookReader extends StatefulWidget {
@override
_BookReaderState createState() => _BookReaderState();
}
class _BookReaderState extends State<BookReader> {
late PageFlipController _controller;
int _currentPage = 0;
final List<Color> _pageColors = [
Colors.red,
Colors.blue,
Colors.green,
Colors.orange,
Colors.purple,
Colors.teal,
];
@override
void initState() {
super.initState();
_controller = PageFlipController();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Book Reader'),
actions: [
IconButton(
icon: Icon(Icons.first_page),
onPressed: () => _controller.goToPage(0),
),
IconButton(
icon: Icon(Icons.arrow_back),
onPressed: _controller.hasPreviousPage
? () => _controller.flipPrev()
: null,
),
Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Center(
child: Text('${_currentPage + 1} / ${_pageColors.length}'),
),
),
IconButton(
icon: Icon(Icons.arrow_forward),
onPressed: _controller.hasNextPage
? () => _controller.flipNext()
: null,
),
IconButton(
icon: Icon(Icons.last_page),
onPressed: () => _controller.goToPage(_pageColors.length - 1),
),
],
),
body: TurnablePage(
controller: _controller,
pageCount: _pageColors.length,
onPageChanged: (leftIndex, rightIndex) {
setState(() {
_currentPage = leftIndex;
});
},
settings: FlipSettings(
showCover: true,
drawShadow: true,
flippingTime: 600,
),
pageBuilder: (index, constraints) {
return Container(
decoration: BoxDecoration(
color: _pageColors[index % _pageColors.length],
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 4,
offset: Offset(2, 2),
),
],
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.auto_stories,
size: 64,
color: Colors.white,
),
SizedBox(height: 20),
Text(
'Page ${index + 1}',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(height: 10),
Text(
'This is the content of page ${index + 1}',
style: TextStyle(
fontSize: 16,
color: Colors.white70,
),
textAlign: TextAlign.center,
),
],
),
),
);
},
),
);
}
}
Architecture #
This package provides the following architecture:
Core Components #
- TurnablePage - Main widget that provides the page-flipping interface
- TurnablePageView - Internal view that handles rendering and animations
- PageFlipController - Controller for programmatic page management
- PageFlip - Core logic engine that handles flip calculations and state
- FlipSettings - Configuration object for customizing behavior
Key Features #
- Widget-Based Pages: Use any Flutter widget as page content through the
pageBuilderfunction - Hardware Acceleration: Leverages Flutter's rendering engine for smooth animations
- Responsive Design: Automatic adaptation between portrait and landscape modes
- Touch Gestures: Full support for swipe, drag, and tap interactions
- Event System: Comprehensive callbacks for page change events
- Customizable Styling: Extensive configuration options for appearance and behavior
Performance Tips #
- Optimize Page Content: Keep page widgets lightweight and avoid heavy computations in
pageBuilder - Use Appropriate Pixel Ratio: Higher values improve quality but impact performance
- Limit Page Count: Very large books may impact memory usage
- Efficient Assets: Keep media lightweight; image-based page examples will be added in a future update
Responsive Design #
// Automatic responsive behavior
TurnablePage(
autoResponseSize: true, // Adapts to device size only in single mode
pageViewMode: PageViewMode.single, // Switches based on screen size
// ...
)
Custom Page Layouts #
Widget buildCustomPage(int index, BoxConstraints constraints) {
return Container(
padding: EdgeInsets.all(16),
child: Column(
children: [
// Header
Container(
height: 60,
child: Text('Chapter $index'),
),
// Content area
Expanded(
child: YourContentWidget(pageIndex: index),
),
// Footer
Container(
height: 40,
child: Text('Page ${index + 1}'),
),
],
),
);
}
Troubleshooting #
Common Issues #
Pages not rendering correctly:
- Ensure
pageCountmatches your actual content - Check that
pageBuilderreturns valid widgets for all indices - Verify container constraints are properly set
Performance issues:
- Reduce
pixelRatioif animations are choppy - Optimize page widget complexity
- Consider lazy loading for large content
Touch gestures not working:
- Ensure
mobileScrollSupportis enabled - Check
swipeDistancethreshold - Verify no conflicting gesture detectors in parent widgets
Layout issues on different screen sizes:
- Use
autoResponseSize: truefor automatic adaptation - Test on various screen sizes and orientations
- Consider using
LayoutBuilderfor custom responsive behavior
Roadmap #
- ✅ Core page flipping logic
- ✅ Widget-based pages
- ✅ Touch/gesture handling
- ✅ Event system and callbacks
- ✅ Hardware-accelerated rendering
- ✅ Responsive design support
- ✅ Portrait/landscape orientation
- ✅ Customizable animations and effects
- ❌ PDF document support
- ❌ Enhanced accessibility features
- ❌ Advanced gesture recognition
- ❌ Bookmark and navigation features
Contributing #
Contributions are welcome! Feel free to open issues and PRs to improve the package.
Development Setup #
- Clone the repository:
git clone https://github.com/saeedahmed725/turnable_page.git
cd turnable_page
- Install dependencies:
flutter pub get
- Run the example:
cd example
flutter run
Guidelines #
- Keep the public API stable when possible and document any changes
- Follow Flutter development best practices
- Include tests for new features
- Update documentation for any API changes
- Ensure backward compatibility
License #
This project is distributed under the Turnable Page Proprietary License (TPPL). Usage, redistribution, and modification are not permitted except via approved pull requests in the official GitHub repository. See the LICENSE file for full terms.
Credits #
- Built with ❤️ for the Flutter community
Support #
If you find this package helpful, please:
- ⭐ Star the repository on GitHub
- 🐛 Report issues on GitHub Issues
- 💡 Suggest features and improvements
- 📖 Contribute to documentation
For support and questions, please use GitHub Issues or start a discussion in the repository.