From d3e91cdb1a4b19b57b979fb0c01c5408d38a10a8 Mon Sep 17 00:00:00 2001 From: mabdc <747455334@qq.com> Date: Sat, 20 Mar 2021 18:51:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=BF=E7=9C=9F=E7=BF=BB=E9=A1=B5=20?= =?UTF-8?q?=E5=8F=82=E8=80=83=E4=BA=86=20https://github.com/fluttercommuni?= =?UTF-8?q?ty/page=5Fturn=20https://github.com/fluttercommunity/page=5Ftur?= =?UTF-8?q?n/pull/15=20https://github.com/fluttercommunity/page=5Fturn/pul?= =?UTF-8?q?l/12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page_turn.dart | 265 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 lib/page_turn.dart diff --git a/lib/page_turn.dart b/lib/page_turn.dart new file mode 100644 index 0000000..f40691f --- /dev/null +++ b/lib/page_turn.dart @@ -0,0 +1,265 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:page_turn/src/builders/index.dart'; + +import 'text_composition.dart'; + +class PageTurn extends StatefulWidget { + PageTurn({ + Key? key, + this.duration = const Duration(milliseconds: 300), + this.cuton = 8, + this.cutoff = 92, + this.backgroundColor = const Color(0xFFFFFFCC), + required this.textComposition, + this.initialIndex = 0, + this.lastPage = const Center( + child: const Text("已经是最后一页"), + ), + this.showDragCutoff = true, + }) : super(key: key); + + final Color backgroundColor; + final TextComposition textComposition; + final Duration duration; + final int initialIndex; + final Widget? lastPage; + final bool showDragCutoff; + final int cutoff; + final int cuton; + + @override + PageTurnState createState() => PageTurnState(); +} + +class PageTurnState extends State with TickerProviderStateMixin { + int pageNumber = 0; + List pages = []; + + List _controllers = []; + bool? _isForward; + + @override + void didUpdateWidget(PageTurn oldWidget) { + if (oldWidget.textComposition != widget.textComposition) { + _setUp(); + } + if (oldWidget.duration != widget.duration) { + _setUp(); + } + if (oldWidget.backgroundColor != widget.backgroundColor) { + _setUp(); + } + super.didUpdateWidget(oldWidget); + } + + @override + void dispose() { + _controllers.forEach((c) => c.dispose()); + super.dispose(); + } + + @override + void initState() { + super.initState(); + _setUp(); + } + + static const BLANK_PAGE = const Center(); + void _setUp() { + _controllers.clear(); + pages.clear(); + for (var i = 0; i < widget.textComposition.pageCount; i++) { + final _controller = AnimationController( + value: 1, + duration: widget.duration, + vsync: this, + ); + _controllers.add(_controller); + final _child = Container( + child: PageTurnWidget( + backgroundColor: widget.backgroundColor, + amount: _controllers[i], + child: widget.textComposition.getPageWidget(pageIndex: i), + ), + ); + pages.add(_child); + } + pages = pages.reversed.toList(); + pageNumber = widget.initialIndex; + } + + bool get _isLastPage => pages.length - 1 == pageNumber; + + bool get _isFirstPage => pageNumber == 0; + + void _turnPage(DragUpdateDetails details, BoxConstraints dimens) { + final _ratio = details.delta.dx / dimens.maxWidth; + if (_isForward == null) { + if (details.delta.dx > 0) { + _isForward = false; + } else { + _isForward = true; + } + } + if (_isForward! || pageNumber == 0) { + _controllers[pageNumber].value += _ratio; + } else { + _controllers[pageNumber - 1].value += _ratio; + } + } + + Future _onDragFinish() async { + if (_isForward != null) { + if (_isForward!) { + if (!_isLastPage && + _controllers[pageNumber].value <= (widget.cutoff / 100 + 0.03)) { + await nextPage(); + } else { + await _controllers[pageNumber].forward(); + } + } else { + if (!_isFirstPage && + _controllers[pageNumber - 1].value >= (widget.cuton / 100 + 0.05)) { + await previousPage(); + } else { + if (_isFirstPage) { + await _controllers[pageNumber].forward(); + } else { + await _controllers[pageNumber - 1].reverse(); + } + } + } + } + _isForward = null; + } + + Future nextPage() async { + _controllers[pageNumber].reverse(); + if (mounted) { + setState(() { + pageNumber++; + }); + } + } + + Future previousPage() async { + if (pageNumber > 0) { + await _controllers[pageNumber - 1].forward(); + if (mounted) + setState(() { + pageNumber--; + }); + } + } + + Future goToPage(int index) async { + if (mounted) + setState(() { + pageNumber = index; + }); + for (var i = 0; i < _controllers.length; i++) { + if (i == index) { + _controllers[i].forward(); + } else if (i < index) { + _controllers[i].reverse(); + } else { + if (_controllers[i].status == AnimationStatus.reverse) + _controllers[i].value = 1; + } + } + } + + @override + Widget build(BuildContext context) { + int i = 0; + return Material( + child: LayoutBuilder( + builder: (context, dimens) => GestureDetector( + behavior: HitTestBehavior.opaque, + onHorizontalDragCancel: () => _isForward = null, + onHorizontalDragUpdate: (details) => _turnPage(details, dimens), + onHorizontalDragEnd: (details) => _onDragFinish(), + child: Stack( + fit: StackFit.expand, + children: [ + if (widget.lastPage != null) widget.lastPage!, + ...pages.map((p) { + i++; + final pn = pages.length - pageNumber; + final ret = + Offstage(offstage: !(i >= pn - 1 && i <= pn + 1), child: p); + return ret; + }).toList(), + Positioned.fill( + child: Flex( + direction: Axis.horizontal, + children: [ + Flexible( + flex: widget.cuton, + child: Container( + color: + pageNumber < 3 ? Colors.green.withAlpha(100) : null, + child: pageNumber < 3 + ? Center( + child: Text("手势上一页", + style: TextStyle(fontSize: 25))) + : null, + ), + ), + Flexible( + flex: 50 - widget.cuton, + child: Container( + color: + pageNumber < 3 ? Colors.blue.withAlpha(100) : null, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: _isFirstPage ? null : previousPage, + child: pageNumber < 3 + ? Center( + child: Text("点击上一页", + style: TextStyle(fontSize: 25))) + : null, + ), + ), + ), + Flexible( + flex: widget.cutoff - 50, + child: Container( + color: + pageNumber < 3 ? Colors.red.withAlpha(100) : null, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: _isLastPage ? null : nextPage, + child: pageNumber < 3 + ? Center( + child: Text("点击下一页", + style: TextStyle(fontSize: 25))) + : null, + ), + ), + ), + Flexible( + flex: 100 - widget.cutoff, + child: Container( + color: + pageNumber < 3 ? Colors.pink.withAlpha(100) : null, + child: pageNumber < 3 + ? Center( + child: Text("手势下一页", + style: TextStyle(fontSize: 25))) + : null, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + } +}