123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'dart:async';
- import 'package:shuqi/public.dart';
- import 'article_provider.dart';
- import 'reader_utils.dart';
- import 'reader_config.dart';
- import 'reader_page_agent.dart';
- import 'reader_menu.dart';
- import 'reader_view.dart';
- enum PageJumpType { stay, firstPage, lastPage }
- class ReaderScene extends StatefulWidget {
- final int articleId;
- ReaderScene({required this.articleId});
- @override
- ReaderSceneState createState() => ReaderSceneState();
- }
- class ReaderSceneState extends State<ReaderScene> with RouteAware {
- int pageIndex = 0;
- bool isMenuVisiable = false;
- PageController pageController = PageController(keepPage: false);
- bool isLoading = false;
- double topSafeHeight = 0;
- Article? preArticle;
- Article? currentArticle;
- Article? nextArticle;
- List<Chapter> chapters = [];
- @override
- void initState() {
- super.initState();
- pageController.addListener(onScroll);
- setup();
- }
- @override
- void didChangeDependencies() {
- super.didChangeDependencies();
- routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute<dynamic>);
- }
- @override
- void didPop() {
- SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
- }
- @override
- void dispose() {
- pageController.dispose();
- routeObserver.unsubscribe(this);
- super.dispose();
- }
- void setup() async {
- await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
- // 不延迟的话,安卓获取到的topSafeHeight是错的。
- await Future.delayed(const Duration(milliseconds: 100), () {});
- SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
- topSafeHeight = Screen.topSafeHeight;
- List<dynamic> chaptersResponse = await Request.get(action: 'catalog');
- chaptersResponse.forEach((data) {
- chapters.add(Chapter.fromJson(data));
- });
- await resetContent(this.widget.articleId, PageJumpType.stay);
- }
- resetContent(int articleId, PageJumpType jumpType) async {
- currentArticle = await fetchArticle(articleId);
- if (currentArticle!.preArticleId > 0) {
- preArticle = await fetchArticle(currentArticle!.preArticleId);
- } else {
- preArticle = null;
- }
- if (currentArticle!.nextArticleId > 0) {
- nextArticle = await fetchArticle(currentArticle!.nextArticleId);
- } else {
- nextArticle = null;
- }
- if (jumpType == PageJumpType.firstPage) {
- pageIndex = 0;
- } else if (jumpType == PageJumpType.lastPage) {
- pageIndex = currentArticle!.pageCount - 1;
- }
- if (jumpType != PageJumpType.stay) {
- pageController.jumpToPage((preArticle != null ? preArticle!.pageCount : 0) + pageIndex);
- }
- setState(() {});
- }
- onScroll() {
- var page = pageController.offset / Screen.width;
- var nextArtilePage = currentArticle!.pageCount + (preArticle != null ? preArticle!.pageCount : 0);
- if (page >= nextArtilePage) {
- print('到达下个章节了');
- preArticle = currentArticle;
- currentArticle = nextArticle;
- nextArticle = null;
- pageIndex = 0;
- pageController.jumpToPage(preArticle!.pageCount);
- fetchNextArticle(currentArticle!.nextArticleId);
- setState(() {});
- }
- if (preArticle != null && page <= preArticle!.pageCount - 1) {
- print('到达上个章节了');
- nextArticle = currentArticle;
- currentArticle = preArticle;
- preArticle = null;
- pageIndex = currentArticle!.pageCount - 1;
- pageController.jumpToPage(currentArticle!.pageCount - 1);
- fetchPreviousArticle(currentArticle!.preArticleId);
- setState(() {});
- }
- }
- fetchPreviousArticle(int articleId) async {
- if (preArticle != null || isLoading || articleId == 0) {
- return;
- }
- isLoading = true;
- preArticle = await fetchArticle(articleId);
- pageController.jumpToPage(preArticle!.pageCount + pageIndex);
- isLoading = false;
- setState(() {});
- }
- fetchNextArticle(int articleId) async {
- if (nextArticle != null || isLoading || articleId == 0) {
- return;
- }
- isLoading = true;
- nextArticle = await fetchArticle(articleId);
- isLoading = false;
- setState(() {});
- }
- Future<Article> fetchArticle(int articleId) async {
- var article = await ArticleProvider.fetchArticle(articleId);
- var contentHeight = Screen.height - topSafeHeight - ReaderUtils.topOffset - Screen.bottomSafeHeight - ReaderUtils.bottomOffset - 20;
- var contentWidth = Screen.width - 15 - 10;
- article.pageOffsets = ReaderPageAgent.getPageOffsets(article.content, contentHeight, contentWidth, ReaderConfig.instance.fontSize);
- return article;
- }
- onTap(Offset position) async {
- double xRate = position.dx / Screen.width;
- if (xRate > 0.33 && xRate < 0.66) {
- SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
- setState(() {
- isMenuVisiable = true;
- });
- } else if (xRate >= 0.66) {
- nextPage();
- } else {
- previousPage();
- }
- }
- onPageChanged(int index) {
- var page = index - (preArticle != null ? preArticle!.pageCount : 0);
- if (page < currentArticle!.pageCount && page >= 0) {
- setState(() {
- pageIndex = page;
- });
- }
- }
- previousPage() {
- if (pageIndex == 0 && currentArticle!.preArticleId == 0) {
- Toast.show('已经是第一页了');
- return;
- }
- pageController.previousPage(duration: Duration(milliseconds: 250), curve: Curves.easeOut);
- }
- nextPage() {
- if (pageIndex >= currentArticle!.pageCount - 1 && currentArticle!.nextArticleId == 0) {
- Toast.show('已经是最后一页了');
- return;
- }
- pageController.nextPage(duration: Duration(milliseconds: 250), curve: Curves.easeOut);
- }
- Widget buildPage(BuildContext context, int index) {
- var page = index - (preArticle != null ? preArticle!.pageCount : 0);
- var article;
- if (page >= this.currentArticle!.pageCount) {
- // 到达下一章了
- article = nextArticle;
- page = 0;
- } else if (page < 0) {
- // 到达上一章了
- article = preArticle;
- page = preArticle!.pageCount - 1;
- } else {
- article = this.currentArticle;
- }
- return GestureDetector(
- onTapUp: (TapUpDetails details) {
- onTap(details.globalPosition);
- },
- child: ReaderView(article: article, page: page, topSafeHeight: topSafeHeight),
- );
- }
- buildPageView() {
- if (currentArticle == null) {
- return Container();
- }
- int itemCount = (preArticle != null ? preArticle!.pageCount : 0) + currentArticle!.pageCount + (nextArticle != null ? nextArticle!.pageCount : 0);
- return PageView.builder(
- physics: BouncingScrollPhysics(),
- controller: pageController,
- itemCount: itemCount,
- itemBuilder: buildPage,
- onPageChanged: onPageChanged,
- );
- }
- buildMenu() {
- if (!isMenuVisiable) {
- return Container();
- }
- return ReaderMenu(
- chapters: chapters,
- articleIndex: currentArticle!.index,
- onTap: hideMenu,
- onPreviousArticle: () {
- resetContent(currentArticle!.preArticleId, PageJumpType.firstPage);
- },
- onNextArticle: () {
- resetContent(currentArticle!.nextArticleId, PageJumpType.firstPage);
- },
- onToggleChapter: (Chapter chapter) {
- resetContent(chapter.id, PageJumpType.firstPage);
- },
- );
- }
- hideMenu() {
- SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
- setState(() {
- this.isMenuVisiable = false;
- });
- }
- @override
- Widget build(BuildContext context) {
- if (currentArticle == null) {
- return Scaffold();
- }
- return Scaffold(
- body: AnnotatedRegion(
- value: SystemUiOverlayStyle.dark,
- child: Stack(
- children: <Widget>[
- Positioned(
- left: 0,
- top: 0,
- right: 0,
- bottom: 0,
- child:
- Image.asset('assets/img/read_bg.png', fit: BoxFit.cover)),
- buildPageView(),
- buildMenu(),
- ],
- ),
- ),
- );
- }
- }
|