flutter 小說(shuō)翻頁(yè)實(shí)現(xiàn)

近期項(xiàng)目收尾,回顧一下flutter的小說(shuō)翻頁(yè)效果以及邏輯的實(shí)現(xiàn).

一、主要邏輯

1、翻頁(yè)效果采用的是AnimationController動(dòng)畫(huà)的方式
2、由于小說(shuō)的章節(jié)比較多, 需要無(wú)限翻頁(yè),采用的是多頁(yè)面進(jìn)行內(nèi)容替換的邏輯
3、難點(diǎn): A頁(yè)面內(nèi)容的加載時(shí)機(jī);B章節(jié)內(nèi)容的加載時(shí)機(jī);C從目錄切換章節(jié);D字體行距的切換;E內(nèi)容分頁(yè)

二、頁(yè)面結(jié)構(gòu)

整個(gè)內(nèi)容采用的是一個(gè)帶有手勢(shì)交互的FutureBuilder視圖, 采用FutureBuilder的好處是可以自然的根據(jù)網(wǎng)絡(luò)請(qǐng)求返回的不同結(jié)果加載對(duì)應(yīng)的數(shù)據(jù),并且不需要手動(dòng)的去setstate

Widget _bodyView() {
    return GestureDetector(
      onTapUp: (TapUpDetails details) {
        double xRate = details.globalPosition.dx / Get.width;
        if (xRate > 0.33 && xRate < 0.66) {
          NovelReadModel read = dataChapterList[currentIndex];
          widget.clickMenu(read);
        } else if (xRate >= 0.66) {
          turnPagenext = true;
          nextPage();
          autoListenTurnPage = false;
        } else {
          // 如果是第一章  則不翻頁(yè)
          if (currentIndex == 1) {
            gbs.shower.toast("已經(jīng)是第一頁(yè)了");
            return;
          }
          turnPagenext = false;
          previousPage();
          autoListenTurnPage = false;
        }
      },
      onPanDown: (details) {
        handsTapDetails = details;
      },
      onHorizontalDragUpdate: _onHorizontalDragUpdate,
      onHorizontalDragEnd: (_) => _onHorizontalDragEnd(),
      onHorizontalDragCancel: _resetAnimation,
      child: FutureBuilder(),// 偽代碼
    );
  }

從代碼中可以看出,
翻頁(yè)不僅僅支持左右滑動(dòng)頁(yè)面onHorizontalDragUpdate方法處理
還可以支持點(diǎn)擊視圖的邊緣onTapUp: (TapUpDetails details) {的方式去切換

WechatIMG3525.jpg

單頁(yè)面內(nèi)容包含了頭部的章節(jié)菜單和底部的文章內(nèi)容
其中內(nèi)容采用的是富文本加載方便聽(tīng)書(shū)時(shí)的進(jìn)度顯示

三、翻頁(yè)

書(shū)籍有很多內(nèi)容, 我們采用一個(gè)數(shù)組L,首次存儲(chǔ)三個(gè)章節(jié)的內(nèi)容(上一章、本章節(jié)、下一章)
翻頁(yè)的重點(diǎn)邏輯在_onHorizontalDragEnd的方法里面,這個(gè)方法是手勢(shì)結(jié)束的方法,.需要通過(guò)手勢(shì)的坐標(biāo)變化來(lái)確定是上一頁(yè)還是下一頁(yè)
1、我們拿下一頁(yè)來(lái)舉例
下一頁(yè)要做一下幾個(gè)事情
(1)、是不是本書(shū)的最后一張最后一頁(yè), 如果是則需要將后面的一頁(yè)替換為完本頁(yè)面
(2)、如果數(shù)組L中沒(méi)有下一章的數(shù)據(jù)(網(wǎng)絡(luò)不佳就會(huì)沒(méi)有下一章),需要進(jìn)行網(wǎng)絡(luò)請(qǐng)求填充下一章
(3)、更新當(dāng)前頁(yè)碼和數(shù)組下標(biāo)
(4)、講停留在頁(yè)面中間的動(dòng)畫(huà)執(zhí)行完畢
(5)、刷新下一頁(yè)內(nèi)容
(6)、統(tǒng)計(jì)埋點(diǎn)
(7)、記錄歷史記錄(存本地?cái)?shù)據(jù)庫(kù))
(8)、根據(jù)需要向服務(wù)器上傳歷史記錄
歷史記錄中涉及到一個(gè)章節(jié)閱讀字?jǐn)?shù)的計(jì)算問(wèn)題, 可以根據(jù)需要取本頁(yè)的第一個(gè)字和最后一個(gè)字中間的字


WechatIMG3527.jpg

刷新上一頁(yè)下一頁(yè)內(nèi)容

pageCenter.autoNext();
      // 更新center頁(yè)面(1、將第二頁(yè)的內(nèi)容替換為第三頁(yè) 2、讓第二頁(yè)返回視覺(jué)上代替第三頁(yè))
await Future.delayed(const Duration(milliseconds: 100));
pageCenter.refresh(_pageCenterChild(dataChapterList[currentIndex].content, dataChapterList[currentIndex], true));
await Future.delayed(const Duration(milliseconds: 200));
pageCenter.noanimaPrevious();
pageBottom.refresh(_pageDefaultChild(dataChapterList[currentIndex + 1].content, dataChapterList[currentIndex + 1], false));

四、切換章節(jié)
切換章節(jié)有多種方案, 主要思路還是替換數(shù)組中的內(nèi)容, 并刷新頁(yè)面
我這里采用了一個(gè)最簡(jiǎn)單粗暴的方案:
1、清空數(shù)組L中所有數(shù)據(jù)
2、重新獲取前后三章的數(shù)據(jù)
3、計(jì)算當(dāng)前章節(jié)第一頁(yè)的下標(biāo)刷新當(dāng)前頁(yè)面內(nèi)容

五、小結(jié)

小說(shuō)翻頁(yè)總體來(lái)講邏輯還是比較復(fù)雜的,
由于功能點(diǎn)比較多又比較碎, 很容易產(chǎn)生各種bug 比如: 切換字體字號(hào)后當(dāng)前頁(yè)面的內(nèi)容刷新問(wèn)題、又比如快速翻頁(yè)到章節(jié)變更的數(shù)據(jù)刷新問(wèn)題
主要整體思路沒(méi)問(wèn)題剩下的只要細(xì)心豆可以搞定

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容