正在做一个小说的滑动列表功能,拉上一章内容,往列表第一位插入数据时,滑动列表跳到了顶部。
下面是参考代码
import 'package:flutter/material.dart';
import 'dart:math' as math;
///聊天列表,添加旧数据和新数据的时候不会导致列表跳动,首尾添加数据不会抖动
class ChatListScrollDemoPage2 extends StatefulWidget {
const ChatListScrollDemoPage2({Key? key}) : super(key: key);
@override
_ChatListScrollDemoPageState2 createState() =>
_ChatListScrollDemoPageState2();
}
class _ChatListScrollDemoPageState2 extends State<ChatListScrollDemoPage2> {
List<ItemData> newData = [
new ItemData(txt: "*********init 1*********", type: "Right"),
new ItemData(txt: "*********init 2*********", type: "Right"),
new ItemData(txt: "*********init 3*********", type: "Right")
];
List<ItemData> loadMoreData = [];
var centerKey = GlobalKey();
var scroller = ScrollController();
final random = math.Random(10);
double extentAfter = 0;
renderRightItem(ItemData data) {
var height = random.nextInt(60);
return Container(
height: 50 + height.toDouble(),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Colors.red,
),
margin: EdgeInsets.only(left: 50, right: 10, top: 5, bottom: 5),
padding: EdgeInsets.all(10),
alignment: Alignment.centerRight,
child: new Text(
data.txt,
maxLines: 10,
),
);
}
renderLeftItem(ItemData data) {
var height = random.nextInt(60);
return Container(
height: 50 + height.toDouble(),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Colors.green,
),
margin: EdgeInsets.only(right: 50, left: 10, top: 5, bottom: 5),
padding: EdgeInsets.all(10),
alignment: Alignment.centerLeft,
child: new Text(
data.txt,
maxLines: 10,
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
newData.add(ItemData(
txt: "#### new Send ${newData.length} #### #### #### #### ",
type: "Right"));
setState(() {});
Future.delayed(Duration(milliseconds: 1000), () {
scroller.jumpTo(scroller.position.maxScrollExtent);
});
},
child: Text(
"Send",
style: TextStyle(color: Colors.yellow),
),
),
appBar: AppBar(
title: new Text("ControllerDemoPage"),
actions: [
new TextButton.icon(
onPressed: () {
final random = math.Random();
int randomInt = random.nextInt(10);
for (int i = 0; i < 20; i++) {
var type = randomInt + i;
if (type % 4 == 0) {
loadMoreData.add(ItemData(
txt: "--------Old ${loadMoreData.length}$i",
type: "Right"));
} else {
loadMoreData.add(ItemData(
txt: "---------Old ${loadMoreData.length}$i",
type: "Left"));
}
}
setState(() {});
},
icon: Icon(
Icons.add,
color: Colors.red,
),
label: Text(
"add old",
style: TextStyle(color: Colors.red),
)),
new TextButton.icon(
onPressed: () {
final random = math.Random();
int randomInt = random.nextInt(10);
for (int i = 0; i < 20; i++) {
var type = randomInt + i * 3;
if (type % 4 == 0) {
newData.add(ItemData(
txt: "########## New ${newData.length} $i",
type: "Left"));
} else {
newData.add(ItemData(
txt: "########## New ${newData.length} $i",
type: "Right"));
}
}
setState(() {});
if (extentAfter == 0) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("你目前位于最底部,自动跳转新消息item"),
duration: Duration(milliseconds: 1000),
));
Future.delayed(Duration(milliseconds: 200), () {
scroller.jumpTo(scroller.position.maxScrollExtent);
Future.delayed(Duration(milliseconds: 400), () {
scroller.jumpTo(scroller.position.maxScrollExtent);
});
});
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: InkWell(
onTap: () {
scroller.jumpTo(scroller.position.maxScrollExtent);
},
child: Container(
height: 50,
width: 200,
color: Colors.blueAccent,
alignment: Alignment.centerLeft,
child: Text("点击我自动跳转新消息item"),
),
),
duration: Duration(milliseconds: 1000),
));
}
},
icon: Icon(
Icons.add,
color: Colors.yellow,
),
label: Text(
"add new",
style: TextStyle(color: Colors.yellow),
)),
///先加载一页满的数据 然后再拉一页老数据
///相当于两个sliverlist都有数据
///然后对底下的sliverlist删除数据 就会出现底下空白
///这里测试删除时,用偏移来解决底部空白
// new TextButton.icon(
// onPressed: () {
// newData.removeLast();
// newData.removeLast();
// newData.removeLast();
// newData.removeLast();
// newData.removeLast();
//
// newData.insert(0, loadMoreData[0]);
// newData.insert(0,loadMoreData[1]);
// newData.insert(0,loadMoreData[2]);
// newData.insert(0,loadMoreData[4]);
// newData.insert(0,loadMoreData[5]);
//
// loadMoreData.removeAt(0);
// loadMoreData.removeAt(1);
// loadMoreData.removeAt(2);
// loadMoreData.removeAt(3);
// loadMoreData.removeAt(4);
//
// setState(() {
//
// });
//
//
// },
// icon: Icon(
// Icons.delete,
// color: Colors.yellow,
// ),
// label: Text(
// "Delete",
// style: TextStyle(color: Colors.yellow),
// )),
],
),
extendBody: true,
body: NotificationListener(
onNotification: (notification) {
if (notification is ScrollNotification) {
if (notification.metrics is PageMetrics) {
return false;
}
if (notification.metrics is FixedScrollMetrics) {
if (notification.metrics.axisDirection == AxisDirection.left ||
notification.metrics.axisDirection == AxisDirection.right) {
return false;
}
}
extentAfter = notification.metrics.extentAfter;
}
return false;
},
child: CustomScrollView(
controller: scroller,
center: centerKey,
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
var item = loadMoreData[index];
if (item.type == "Right")
return renderRightItem(item);
else
return renderLeftItem(item);
},
childCount: loadMoreData.length,
),
),
SliverPadding(
padding: EdgeInsets.zero,
key: centerKey,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
var item = newData[index];
if (item.type == "Right")
return renderRightItem(item);
else
return renderLeftItem(item);
},
childCount: newData.length,
),
),
],
),
),
);
}
}
class ItemData {
String txt = "";
String type = "";
ItemData({
required this.txt,
required this.type,
});
}
//小说滑动列表代码,往上滑的时候跳转到顶部,处理:跳回到上一个位置,缺点是会闪一下
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:loading_indicator/loading_indicator.dart';
import 'package:social_im/pages/read_book/buy_novel_dialog.dart';
import '../../common/Global.dart';
import '../../common/colors.dart';
import '../../common/globalEventBus.dart';
import '../../config/shared_preferences_helper.dart';
import '../../http/api.dart';
import '../../http/net_callback.dart';
import '../../http/rxhttp.dart';
import '../../http/utils/NetUtils.dart';
import '../../http/utils/response.dart';
import 'novelModel/novel_chapter_content.dart';
import 'novelModel/novel_details_model.dart';
import 'novelModel/save_novel_data.dart';
//小说阅读页
class ReadPageView extends StatefulWidget {
ChapterInfo? chapterInfo;
String? novelName;
ReadPageView(this.chapterInfo, this.novelName, {Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => ReadPageViewState();
}
class ReadPageViewState extends State<ReadPageView>{
//列表控制器
final ScrollController _scrollController = ScrollController();
//还有更多数据
bool hasMore = true;
//上划的章数
int startPage = 0;
//最开始的章数
int firstPage = 0;
//下滑的章数
int endPage = 0;
//点击的位置
late Offset startPosition;
//顶部布局的高度
final GlobalKey _globalKey = GlobalKey();
//数据列表的高度
GlobalKey listGlobalKey = GlobalKey();
//数据列表的高度小于满屏高度减顶部高度时,填充一个占位高度,确保可以滑动
double seatHeight = 0.0;
//是否显示占位符
bool showSeat = false;
//小说内容列表
List<ChapterInfo> novelChapterContentList = [];
//扣费弹窗已弹出,不可以再弹出来了
StreamSubscription<NovelDialogHasShow>? novelDialogHasShowEvent;
//已显示小说扣费弹窗
bool hasShowNovelFreeDialog = false;
//是否显示底部loading
bool showLoading = false;
//是否正在进行网络请求
bool hasHttpRes = false;
///一秒内只监听列表滑动一次划到低部,防止多次请求
bool scrollListener = false;
@override
void initState() {
//扣费弹窗已弹出来,不可以再弹了
novelDialogHasShowEvent = EventBusUtil.listen((event) {
if (event.hasShow) {
hasShowNovelFreeDialog = true;
} else {
hasShowNovelFreeDialog = false;
}
});
//显示loading
showLoading = true;
//获取小说数据
getNovelData(widget.chapterInfo?.bookId ?? 1,
widget.chapterInfo?.chapter ?? 1, false,
initGetData: true);
//标记最开始的章节
startPage = widget.chapterInfo?.chapter ?? 1;
if (startPage >= 2) {
//上一章的章节数等于传进来的章节数减一
startPage = startPage - 1;
}
//标记最末尾的章节
endPage = widget.chapterInfo?.chapter ?? 1;
// 监听滚动事件:注意:请求数据的时候不可能让页数+1了
//拉到最底部,请求下一章数据
_scrollController.addListener(() {
//防止一次监听到两次滑到最底部
if (scrollListener == false) {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
//已经拉到一次底部了
scrollListener = true;
//达到最大滚动位置
if (hasMore) {
mySetState(() {
//显示loading
showLoading = true;
//不是往顶部添加数据
bool start = false;
//要在添加数据之前跳转,不然就会出现,添加数据完成后直接跳到末尾了
if (_scrollController.hasClients) {
_scrollController
.jumpTo(_scrollController.position.maxScrollExtent - 1);
}
//请求数据
getNovelData(widget.chapterInfo?.bookId ?? 1, endPage, start);
});
}
//延时一秒才可以再监听滑到底部
Future.delayed(const Duration(milliseconds: 1000), () {
scrollListener = false;
});
}
}else{
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent){
if (_scrollController.hasClients) {
_scrollController
.jumpTo(_scrollController.position.maxScrollExtent - 1);
}
}
}
//拉到最顶部,请求上一章
if (_scrollController.position.pixels ==
_scrollController.position.minScrollExtent) {
if (getListHeight() != null) {
//(一进来的数据就满屏)列表的高度大于屏幕高度,才往上挪一下
if (getListHeight()! > Global.screenHeight) {
if (_scrollController.hasClients) {
_scrollController
.jumpTo(_scrollController.position.minScrollExtent + 1);
}
} else if (getListHeight()! + seatHeight > Global.screenHeight) {
//(一进来的数据没满屏),列表的高度+填充高度大于满屏就网上挪一下
if (showSeat == true) {
if (_scrollController.hasClients) {
_scrollController
.jumpTo(_scrollController.position.minScrollExtent + 1);
}
}
}
}
//往顶部添加数据
bool start = true;
//大于等于第一章才请求
if (hasMore) {
mySetState(() {
if (startPage >= 1) {
getNovelData(widget.chapterInfo?.bookId ?? 1, startPage, start);
}
});
}
}
});
super.initState();
}
mySetState(callBack) {
if (mounted) {
setState(() {
callBack();
});
}
}
//获取小说具体内容
getNovelData(int bookId, int chapter, bool isStart, {bool? initGetData}) {
RxHttp<Response>()
..init()
..setBaseUrl(Api.BUSINESS_BASE_API)
..setPath(Api.API_BOOK_CONTENT)
..setCacheMode(CacheMode.FIRST_CACHE_THEN_REQUEST)
..setJsonTransFrom((p0) => Response.fromJson(json.decoder.convert(p0)))
..setParams({
"book_id": bookId,
"chapter": chapter,
})
..call(
NetCallback(onNetFinish: (response) {
//加延时防止滑动列表短时间重复请求
if (response.code == 0 || response.code == 200) {
//为false就处理数据
log('小说数据11112 ${json.encode(response.data)}');
NovelChapterContent? novelChapterContent =
NovelChapterContent.fromJson(response.data);
handleNovel(novelChapterContent, isStart, initGetData);
if (mounted) {
setState(() {});
}
}
}, onCacheFinish: (response) {
if (response.code == 0 || response.code == 200) {
NovelChapterContent? novelChapterContent =
NovelChapterContent.fromJson(response.data);
log('小说数据1111 ${json.encode(response.data)}');
//如果该章节需要扣费,且还没有购买,就不执行
if (novelChapterContent.chapterInfo != null) {
if (novelChapterContent.chapterInfo!.freeChapter != null) {
//免费的就让缓存执行
if (novelChapterContent.chapterInfo!.freeChapter == 1) {
handleNovel(novelChapterContent, isStart, initGetData);
} else {
//收费但是已经购买的可以让缓存执行
if (novelChapterContent.chapterInfo!.isBuy != null) {
//已经购买
if (novelChapterContent.chapterInfo!.isBuy == 1) {
handleNovel(novelChapterContent, isStart, initGetData);
}
}
}
}
}
if (mounted) {
setState(() {});
}
}
}, onNetError: (errorCode, error) {
showLoading = false;
mySetState(() {});
// showToast('$error ($errorCode)');
}),
server: Servers.businessServer);
}
//点进去需要扣费,缓存需要扣费,购买过小说后再请求一次,刷新缓存,免得进来还是先拿缓存还要扣费
reGetNovelData(int bookId, int chapter) {
if (chapter <= 0) {
return;
}
RxHttp<Response>()
..init()
..setBaseUrl(Api.BUSINESS_BASE_API)
..setPath(Api.API_BOOK_CONTENT)
..setCacheMode(CacheMode.REQUEST_FAILED_READ_CACHE)
..setJsonTransFrom((p0) => Response.fromJson(json.decoder.convert(p0)))
..setParams({
"book_id": bookId,
"chapter": chapter,
})
..call(
NetCallback(
onNetFinish: (response) {},
onCacheFinish: (response) {},
onNetError: (errorCode, error) {}),
server: Servers.businessServer);
}
//添加数据时,查找当前列表是否有重复数据
checkListData(NovelChapterContent? novelChapterContent) {
bool isHasData = false;
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
for (int i = 0; i < novelChapterContentList.length; i++) {
if (novelChapterContentList[i].bookId ==
novelChapterContent.chapterInfo!.bookId) {
if (novelChapterContentList[i].chapter ==
novelChapterContent.chapterInfo!.chapter) {
isHasData = true;
}
}
}
}
}
return isHasData;
}
//处理小说扣费
//添加数据时检查列表是否够满屏,没满屏的话显示控件
handleNovel(
NovelChapterContent? novelChapterContent, bool isStart, bool? getData) {
showLoading = false;
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
//contains去重
if (!novelChapterContentList
.contains(novelChapterContent.chapterInfo)) {
//遍历去重,其实应该要删掉旧的数据(缓存),添加新的数据(网络)
if (checkListData(novelChapterContent) == false) {
if (isStart) {
//往顶部插入数据
//不免费章节
if (novelChapterContent.chapterInfo!.freeChapter == 2) {
//判断是否已购买
if (novelChapterContent.chapterInfo!.isBuy == 2) {
//已经弹出扣费弹窗了,就不再弹
if (hasShowNovelFreeDialog) {
} else {
showModalBottomSheet(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24))),
isScrollControlled: true,
enableDrag: false,
backgroundColor: CommonColors.getColorF5F8F5,
context: context,
builder: (context) =>
BuyNovelDialog(novelChapterContent)).then((value) {
if (value != null) {
if (value == true) {
double extentAfter =
_scrollController.position.extentAfter;
if (checkListData(novelChapterContent) == false) {
novelChapterContentList.insert(
0, novelChapterContent.chapterInfo!);
startPage = startPage - 1;
}
Future.delayed(const Duration(milliseconds: 100), () {
if (_scrollController.position.maxScrollExtent -
extentAfter <
Global.screenHeight) {
//插入数据后跳转到上一章的底部,衔接阅读
if (_scrollController.hasClients) {
//+1防止跳到顶部又请求上一章了
_scrollController.jumpTo(
_scrollController.position.minScrollExtent+1);
}
} else {
if (_scrollController.hasClients) {
_scrollController.jumpTo(
_scrollController.position.maxScrollExtent -
extentAfter -
Global.screenHeight +
(Global.topHeight + 30 + 20));
}
}
});
//重新请求一下数据,刷新缓存数据
reGetNovelData(
widget.chapterInfo?.bookId ?? 1, startPage + 1);
//删除原来的记录,保存新的记录,查询
deleteOldUserIdAndChapter(novelChapterContent);
savaUserIdAndChapter(novelChapterContent);
checkNovelData(novelChapterContent);
mySetState(() {});
Future.delayed(const Duration(milliseconds: 100), () {
addDataCheckScreen();
});
}
} else {
//不购买章节,划到顶部页数就-1了,实际上因为弹窗问题,数据没添加到,所以要把页数加回来
}
});
}
} else {
//已购买过章节
double extentAfter = _scrollController.position.extentAfter;
novelChapterContentList.insert(
0, novelChapterContent.chapterInfo!);
startPage = startPage - 1;
Future.delayed(const Duration(milliseconds: 100), () {
//跳转的时候减了extentAfter,还要判断剩余屏幕高度是否大于Global.screenHeight,不大于就直接跳到最开始位置,大于就跳到下面到公式
if (_scrollController.position.maxScrollExtent -
extentAfter <
Global.screenHeight) {
if (_scrollController.hasClients) {
//+1防止跳到顶部又请求上一章了
_scrollController
.jumpTo(_scrollController.position.minScrollExtent+1);
}
} else {
if (_scrollController.hasClients) {
_scrollController.jumpTo(
_scrollController.position.maxScrollExtent -
extentAfter -
Global.screenHeight +
(Global.topHeight + 30 + 20));
}
}
});
//删除原来的记录,保存新的记录,查询
deleteOldUserIdAndChapter(novelChapterContent);
savaUserIdAndChapter(novelChapterContent);
checkNovelData(novelChapterContent);
mySetState(() {});
Future.delayed(const Duration(milliseconds: 100), () {
addDataCheckScreen();
});
}
} else {
//免费章节
double extentAfter = _scrollController.position.extentAfter;
novelChapterContentList.insert(
0, novelChapterContent.chapterInfo!);
startPage = startPage - 1;
Future.delayed(const Duration(milliseconds: 100), () {
if (_scrollController.position.maxScrollExtent - extentAfter <
Global.screenHeight) {
if (_scrollController.hasClients) {
//+1防止跳到顶部又请求上一章了
_scrollController
.jumpTo(_scrollController.position.minScrollExtent+1);
}
} else {
if (_scrollController.hasClients) {
_scrollController.jumpTo(
_scrollController.position.maxScrollExtent -
extentAfter -
Global.screenHeight +
(Global.topHeight + 30 + 20));
}
}
});
//删除原来的记录,保存新的记录,查询
deleteOldUserIdAndChapter(novelChapterContent);
savaUserIdAndChapter(novelChapterContent);
checkNovelData(novelChapterContent);
mySetState(() {});
Future.delayed(const Duration(milliseconds: 100), () {
addDataCheckScreen();
});
}
} else {
//往底部插入数据
//不免费章节
if (novelChapterContent.chapterInfo!.freeChapter == 2) {
//判断是否已购买
if (novelChapterContent.chapterInfo!.isBuy == 2) {
if (hasShowNovelFreeDialog) {
} else {
showModalBottomSheet(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24))),
isScrollControlled: true,
enableDrag: false,
backgroundColor: CommonColors.getColorF5F8F5,
context: context,
builder: (context) =>
BuyNovelDialog(novelChapterContent)).then((value) {
if (value != null) {
if (value == true) {
if (checkListData(novelChapterContent) == false) {
novelChapterContentList
.add(novelChapterContent.chapterInfo!);
endPage = endPage + 1;
}
//如果列表大于屏幕,一进来就预留顶部位置
Future.delayed(const Duration(milliseconds: 100), () {
if (getListHeight() != null) {
if (getListHeight()! >= Global.screenHeight) {
if (_scrollController.hasClients) {
_scrollController.jumpTo(_scrollController
.position.minScrollExtent +
1);
}
}
}
});
reGetNovelData(
widget.chapterInfo?.bookId ?? 1, endPage - 1);
//删除原来的记录,保存新的记录,查询
deleteOldUserIdAndChapter(novelChapterContent);
savaUserIdAndChapter(novelChapterContent);
checkNovelData(novelChapterContent);
mySetState(() {});
Future.delayed(const Duration(milliseconds: 100), () {
addDataCheckScreen();
});
}
} else {
//检查列表是否满屏
if (getData != null) {
if (getData = true) {
Future.delayed(const Duration(milliseconds: 100),
() {
addDataCheckScreen();
});
//章数直接传进来的就不要减了,因为没有加到
}
} else {
Future.delayed(const Duration(milliseconds: 100), () {
addDataCheckScreen();
});
// endPage = endPage - 1;
}
}
});
}
} else {
//已经购买过该章节
novelChapterContentList.add(novelChapterContent.chapterInfo!);
endPage = endPage + 1;
if (getData != null) {
if (getData == true) {
//一进来就预留顶部位置
Future.delayed(const Duration(milliseconds: 100), () {
if (getListHeight() != null) {
if (getListHeight()! >= Global.screenHeight) {
if (_scrollController.hasClients) {
_scrollController.jumpTo(
_scrollController.position.minScrollExtent +
1);
}
}
}
});
}
}
//删除原来的记录,保存新的记录,查询
deleteOldUserIdAndChapter(novelChapterContent);
savaUserIdAndChapter(novelChapterContent);
checkNovelData(novelChapterContent);
mySetState(() {});
Future.delayed(const Duration(milliseconds: 100), () {
addDataCheckScreen();
});
}
} else {
//免费章节
novelChapterContentList.add(novelChapterContent.chapterInfo!);
if (getData != null) {
if (getData == true) {
//一进来就预留顶部位置
Future.delayed(const Duration(milliseconds: 100), () {
if (getListHeight() != null) {
if (getListHeight()! >= Global.screenHeight) {
if (_scrollController.hasClients) {
_scrollController.jumpTo(
_scrollController.position.minScrollExtent + 1);
}
}
}
});
}
}
endPage = endPage + 1;
//删除原来的记录,保存新的记录,查询
deleteOldUserIdAndChapter(novelChapterContent);
savaUserIdAndChapter(novelChapterContent);
checkNovelData(novelChapterContent);
mySetState(() {});
Future.delayed(const Duration(milliseconds: 100), () {
addDataCheckScreen();
});
}
}
}
}
}
}
}
//本地删除该书的旧章节
deleteOldUserIdAndChapter(NovelChapterContent? novelChapterContent) {
List<String> saveNovelDataList = [];
if (SPHelper.getNovelDataList == null) {
SPHelper.setNovelDataList([]);
if (SPHelper.getNovelDataList != null) {
saveNovelDataList = SPHelper.getNovelDataList;
for (int i = 0; i < saveNovelDataList.length; i++) {
SaveNovelData saveNovelData =
SaveNovelData.fromJson(jsonDecode(saveNovelDataList[i]));
if (saveNovelData.userId == Global.userId) {
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
if (saveNovelData.chapterInfo != null) {
if (novelChapterContent.chapterInfo!.bookId ==
saveNovelData.chapterInfo!.bookId) {
//保存有该书数据就删除
saveNovelDataList.removeAt(i);
}
}
}
}
}
}
//重新赋值
SPHelper.setNovelDataList(saveNovelDataList);
}
} else {
saveNovelDataList = SPHelper.getNovelDataList;
for (int i = 0; i < saveNovelDataList.length; i++) {
SaveNovelData saveNovelData =
SaveNovelData.fromJson(jsonDecode(saveNovelDataList[i]));
if (saveNovelData.userId == Global.userId) {
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
if (saveNovelData.chapterInfo != null) {
if (novelChapterContent.chapterInfo!.bookId ==
saveNovelData.chapterInfo!.bookId) {
//保存有该书数据就删除
saveNovelDataList.removeAt(i);
}
}
}
}
}
}
//重新赋值
SPHelper.setNovelDataList(saveNovelDataList);
}
}
//查询本地保存的小说记录,仅供自己使用
checkNovelData(NovelChapterContent? novelChapterContent) {
List<String> saveNovelDataList = [];
if (SPHelper.getNovelDataList == null) {
SPHelper.setNovelDataList([]);
if (SPHelper.getNovelDataList != null) {
saveNovelDataList = SPHelper.getNovelDataList;
for (int i = 0; i < saveNovelDataList.length; i++) {
SaveNovelData saveNovelData =
SaveNovelData.fromJson(jsonDecode(saveNovelDataList[i]));
if (saveNovelData.userId == Global.userId) {
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
if (saveNovelData.chapterInfo != null) {
if (novelChapterContent.chapterInfo!.bookId ==
saveNovelData.chapterInfo!.bookId) {}
}
}
}
}
}
//重新赋值
SPHelper.setNovelDataList(saveNovelDataList);
}
} else {
saveNovelDataList = SPHelper.getNovelDataList;
for (int i = 0; i < saveNovelDataList.length; i++) {
SaveNovelData saveNovelData =
SaveNovelData.fromJson(jsonDecode(saveNovelDataList[i]));
if (saveNovelData.userId == Global.userId) {
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
if (saveNovelData.chapterInfo != null) {
if (novelChapterContent.chapterInfo!.bookId ==
saveNovelData.chapterInfo!.bookId) {}
}
}
}
}
}
//重新赋值
SPHelper.setNovelDataList(saveNovelDataList);
}
}
//本地添加该书的新章节记录
savaUserIdAndChapter(NovelChapterContent? novelChapterContent) {
if (novelChapterContent != null) {
List<String> saveNovelDataList = [];
//获取已经存好的数据
if (SPHelper.getNovelDataList == null) {
SPHelper.setNovelDataList([]);
if (SPHelper.getNovelDataList != null) {
saveNovelDataList = SPHelper.getNovelDataList;
SaveNovelData saveNovelData = SaveNovelData(
userId: Global.userId,
chapterInfo: novelChapterContent.chapterInfo);
saveNovelDataList.add(jsonEncode(saveNovelData.toJson()));
SPHelper.setNovelDataList(saveNovelDataList);
}
} else {
saveNovelDataList = SPHelper.getNovelDataList;
SaveNovelData sayHiData = SaveNovelData(
userId: Global.userId,
chapterInfo: novelChapterContent.chapterInfo);
saveNovelDataList.add(jsonEncode(sayHiData.toJson()));
SPHelper.setNovelDataList(saveNovelDataList);
}
}
}
//点击屏幕上边和下边
void _onTapDown(TapUpDetails details) async {
/// 上边触摸
startPosition = details.globalPosition;
//频幕高度
double layoutHeight = Global.screenHeight;
if (startPosition.dy < (layoutHeight / 2)) {
if (_scrollController.position.pixels -
_scrollController.position.minScrollExtent >=
Global.screenHeight) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.pixels -
Global.screenHeight +
(Global.topHeight + 30 + 20),
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
} else {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.minScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
}
} else {
/// 下边触摸
if (_scrollController.position.maxScrollExtent -
_scrollController.position.pixels >=
Global.screenHeight) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.pixels +
Global.screenHeight -
(Global.topHeight + 30 + 20),
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
} else {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
}
}
}
//点击占位控件
seatWidgetTap(TapUpDetails details){
/// 上边触摸
startPosition = details.globalPosition;
//频幕高度
double layoutHeight = Global.screenHeight;
if (startPosition.dy < (layoutHeight / 2)) {
if (_scrollController.position.pixels -
_scrollController.position.minScrollExtent >=
Global.screenHeight) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.pixels -
Global.screenHeight +
(Global.topHeight + 30 + 20),
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
} else {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.minScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
}
} else {
/// 下边触摸
if (_scrollController.position.maxScrollExtent -
_scrollController.position.pixels >=
Global.screenHeight) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.pixels +
Global.screenHeight -
(Global.topHeight + 30 + 20),
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
} else {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
}
}
}
@override
void dispose() {
_scrollController.dispose();
if (novelDialogHasShowEvent != null) {
novelDialogHasShowEvent!.cancel();
}
super.dispose();
}
//没有满屏,滑动列表的时候
//用了一次就失效了,可能是因为控制权被SingleChildScrollView的控制器抢走了
_onVerticalDragStart(DragUpdateDetails details) {
final containerHeight = _globalKey.currentContext?.size?.height;
final listHeight = listGlobalKey.currentContext
?.findRenderObject()
?.semanticBounds
.size
.height;
if (containerHeight != null) {
if (listHeight != null) {
if (containerHeight + listHeight < Global.screenHeight) {
seatHeight = Global.screenHeight - containerHeight - listHeight + 10;
showSeat = true;
mySetState(() {});
}
}
}
}
///检查内容高度是否大于屏幕高度,不大于就填充一个控件,保证列表可以滑动
addDataCheckScreen() {
//顶部高度
final containerHeight = _globalKey.currentContext?.size?.height;
//实际列表高度
final listHeight = listGlobalKey.currentContext
?.findRenderObject()
?.semanticBounds
.size
.height;
if (containerHeight != null) {
if (listHeight != null) {
//如果总的列表高度-前一个填充高度还是小于屏幕高度减顶部高度,重新计算填充的高度
if (listHeight - seatHeight <= Global.screenHeight - containerHeight) {
//全屏减顶部等于列表高度,如果第一次进来没满屏幕,就填充
if (listHeight <= Global.screenHeight - containerHeight) {
seatHeight =
Global.screenHeight - containerHeight - listHeight + 10;
} else {
//加上占位已经满屏幕了,但数据没满屏幕,重新计算占位高度。或者数据本身已经满屏幕了这个就可以隐藏占位符
if (listHeight - seatHeight <=
Global.screenHeight - containerHeight) {
//实际列表高度小于屏幕高度
seatHeight = Global.screenHeight -
containerHeight -
(listHeight - seatHeight) +
10;
} else {
//实际列表高度大于屏幕高度
showSeat = false;
mySetState(() {});
}
}
showSeat = true;
mySetState(() {});
} else {
//隐藏占位
showSeat = false;
mySetState(() {});
}
if (showSeat == true) {
//数据还没满屏,要让顶部有距离
if (_scrollController.hasClients) {
//添加数据后,当前位置等于0,即在顶部的时候才预留顶部位置
if(_scrollController.position.pixels==_scrollController.position.minScrollExtent){
_scrollController
.jumpTo(_scrollController.position.minScrollExtent + 1);
}
}
}
}
}
}
//返回列表高度
double? getListHeight() {
double? listH;
final listHeight = listGlobalKey.currentContext
?.findRenderObject()
?.semanticBounds
.size
.height;
if (listHeight != null) {
listH = listHeight;
}
return listH;
}
@override
Widget build(BuildContext context) {
return Material(
child: Stack(
alignment: Alignment.bottomCenter,
children: [
Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/bg_novel_read.png'),
fit: BoxFit.fill // 完全填充
)),
padding: EdgeInsets.only(bottom: 20.r),
child: GestureDetector(
// onVerticalDragUpdate: _onVerticalDragStart,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
addHeadView(),
Expanded(
child: SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.vertical,
child: Column(
key: listGlobalKey,
mainAxisAlignment: MainAxisAlignment.start,
children: [
MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView.builder(
itemCount:
novelChapterContentList.length,
physics:
const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder:
(BuildContext context, int index) {
return GestureDetector(
onTapUp: _onTapDown,
child: Container(
margin: EdgeInsets.only(
left: 16.r, right: 16.r),
padding:
EdgeInsets.only(bottom: 20.r),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
children: [
//下拉加载下一页,列表拼接,显示对象里的文本字段
Container(
alignment:
Alignment.centerLeft,
padding: EdgeInsets.only(
bottom: 15.0.r,
top: 30.r),
child: Text(
novelChapterContentList[
index]
.title ??
'',
style: TextStyle(
color: const Color(
0xFF222222),
fontSize: 24.sp),
),
),
Text(
novelChapterContentList[
index]
.content ??
'',
style: TextStyle(
color: const Color(
0xFF222222),
fontSize: 20.sp,
height: 2.2,
letterSpacing: 1.5)),
],
),
),
);
},
),
),
if (showSeat)
GestureDetector(
onTapUp: seatWidgetTap,
child: Container(
height: seatHeight,
color: Colors.transparent,
),
)
]))),
]),
)),
showLoading
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.only(bottom: 3.r),
alignment: Alignment.center,
width: 16.r,
height: 16.r,
child: const LoadingIndicator(
indicatorType: Indicator.circleStrokeSpin,
/// Required, The loading type of the widget
colors: [Color(0xFFFF4E68)],
/// Optional, The color collections
strokeWidth: 2,
/// Optional, The stroke of the line, only applicable to widget which contains line
backgroundColor: Colors.transparent,
/// Optional, Background of the widget
pathBackgroundColor: Color(0xCCFFFFFF)
/// Optional, the stroke backgroundColor
),
),
],
)
: Container()
],
),
);
}
Widget addHeadView() {
return Container(
key: _globalKey,
padding:
EdgeInsets.only(top: Global.topHeight + 10, left: 12.r, right: 5.r),
child: Row(children: [
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: const Image(
image: AssetImage('assets/images/icon_back@2x.png'),
width: 24,
height: 25,
color: Colors.black,
),
),
LimitedBox(
maxWidth: Global.screenWidth - 50,
child: Text(
"${widget.novelName}",
style: const TextStyle(color: Color(0xFF222222), fontSize: 16),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
)
]));
}
}
用自定义列表写的小说代码,上下两个列表,顶部为分割线,上无穷,下无穷,就不会跳跃了
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:loading_indicator/loading_indicator.dart';
import 'package:social_im/pages/read_book/buy_novel_dialog.dart';
import 'package:social_im/utils/printUtil.dart';
import '../../common/Global.dart';
import '../../common/colors.dart';
import '../../common/globalEventBus.dart';
import '../../config/shared_preferences_helper.dart';
import '../../http/api.dart';
import '../../http/net_callback.dart';
import '../../http/rxhttp.dart';
import '../../http/utils/NetUtils.dart';
import '../../http/utils/response.dart';
import 'novelModel/novel_chapter_content.dart';
import 'novelModel/novel_details_model.dart';
import 'novelModel/save_novel_data.dart';
//小说阅读页
class ReadPageNew extends StatefulWidget {
ChapterInfo? chapterInfo;
String? novelName;
ReadPageNew(this.chapterInfo, this.novelName, {Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => ReadPageNewState();
}
class ReadPageNewState extends State<ReadPageNew> {
//列表控制器
final ScrollController _scrollController = ScrollController();
//还有更多数据
bool hasMore = true;
//上划的章数
int startPage = 0;
//最开始的章数
int firstPage = 0;
//下滑的章数
int endPage = 0;
//点击的位置
late Offset startPosition;
//顶部布局的高度,59
final GlobalKey _globalKey = GlobalKey();
//底部数据列表的高度
GlobalKey listGlobalDownKey = GlobalKey();
//顶部数据列表的高度
GlobalKey listGlobalUpKey = GlobalKey();
//设置列表中心位置
var centerKey = GlobalKey();
//数据列表的高度小于满屏高度减顶部高度时,填充一个占位高度,确保可以滑动
double seatHeight = 0.0;
//是否显示占位符
bool showSeat = false;
//小说底部列表内容
List<ChapterInfo> novelContentDownList = [];
//小说顶部列表内容
List<ChapterInfo> novelContentUpList = [];
//扣费弹窗已弹出,不可以再弹出来了
StreamSubscription<NovelDialogHasShow>? novelDialogHasShowEvent;
//已显示小说扣费弹窗
bool hasShowNovelFreeDialog = false;
//是否显示底部loading
bool showLoading = false;
//是否正在进行网络请求
bool hasHttpRes = false;
///一秒内只监听列表滑动一次划到低部,防止多次请求
bool scrollListener = false;
//头部距离列表的距离
double paddingSize = 20.0;
@override
void initState() {
//扣费弹窗已弹出来,不可以再弹了
novelDialogHasShowEvent = EventBusUtil.listen((event) {
if (event.hasShow) {
hasShowNovelFreeDialog = true;
} else {
hasShowNovelFreeDialog = false;
}
});
//显示loading
showLoading = true;
//获取小说数据
getNovelData(widget.chapterInfo?.bookId ?? 1,
widget.chapterInfo?.chapter ?? 1, false,
initGetData: true);
//标记最开始的章节
startPage = widget.chapterInfo?.chapter ?? 1;
if (startPage >= 2) {
//上一章的章节数等于传进来的章节数减一
startPage = startPage - 1;
}
//标记最末尾的章节
endPage = widget.chapterInfo?.chapter ?? 1;
// 监听滚动事件:注意:请求数据的时候不可能让页数+1了
//拉到最底部,请求下一章数据
_scrollController.addListener(() {
//防止一次监听到两次滑到最底部
if (scrollListener == false) {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
//已经拉到一次底部了
scrollListener = true;
//达到最大滚动位置
if (hasMore) {
mySetState(() {
//显示loading
showLoading = true;
//不是往顶部添加数据
bool start = false;
//要在添加数据之前跳转,不然就会出现,添加数据完成后直接跳到末尾了
if (_scrollController.hasClients) {
_scrollController
.jumpTo(_scrollController.position.maxScrollExtent - 1);
}
//请求数据
getNovelData(widget.chapterInfo?.bookId ?? 1, endPage, start);
});
}
//延时一秒才可以再监听滑到底部
Future.delayed(const Duration(milliseconds: 1000), () {
scrollListener = false;
});
}
} else {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
if (_scrollController.hasClients) {
_scrollController
.jumpTo(_scrollController.position.maxScrollExtent - 1);
}
}
}
//拉到最顶部,请求上一章
if (_scrollController.position.pixels ==
_scrollController.position.minScrollExtent) {
//这个监听列表的高度最大只能是该屏幕列表占的最大实际高度,20是顶部padding,顶部头部布局,剩下的就是列表高度了
if (getDoubleListHeight() >=
Global.screenHeight - paddingSize - getHeadHeight()) {
if (_scrollController.hasClients) {
_scrollController
.jumpTo(_scrollController.position.minScrollExtent + 1);
}
} else if (getDoubleListHeight() + seatHeight >=
Global.screenHeight - paddingSize - getHeadHeight()) {
//(一进来的数据没满屏),列表的高度+填充高度大于满屏就网上挪一下
if (showSeat == true) {
if (_scrollController.hasClients) {
_scrollController
.jumpTo(_scrollController.position.minScrollExtent + 1);
}
}
}
//往顶部添加数据
bool start = true;
//大于等于第一章才请求
if (hasMore) {
mySetState(() {
if (startPage >= 1) {
getNovelData(widget.chapterInfo?.bookId ?? 1, startPage, start);
}
});
}
}
});
super.initState();
}
mySetState(callBack) {
if (mounted) {
setState(() {
callBack();
});
}
}
//获取小说具体内容
getNovelData(int bookId, int chapter, bool isStart, {bool? initGetData}) {
RxHttp<Response>()
..init()
..setBaseUrl(Api.BUSINESS_BASE_API)
..setPath(Api.API_BOOK_CONTENT)
..setCacheMode(CacheMode.FIRST_CACHE_THEN_REQUEST)
..setJsonTransFrom((p0) => Response.fromJson(json.decoder.convert(p0)))
..setParams({
"book_id": bookId,
"chapter": chapter,
})
..call(
NetCallback(onNetFinish: (response) {
//加延时防止滑动列表短时间重复请求
if (response.code == 0 || response.code == 200) {
//为false就处理数据
log('小说数据11112 ${json.encode(response.data)}');
NovelChapterContent? novelChapterContent =
NovelChapterContent.fromJson(response.data);
handleNovel(novelChapterContent, isStart, initGetData);
if (mounted) {
setState(() {});
}
}
}, onCacheFinish: (response) {
if (response.code == 0 || response.code == 200) {
NovelChapterContent? novelChapterContent =
NovelChapterContent.fromJson(response.data);
log('小说数据1111 ${json.encode(response.data)}');
//如果该章节需要扣费,且还没有购买,就不执行
if (novelChapterContent.chapterInfo != null) {
if (novelChapterContent.chapterInfo!.freeChapter != null) {
//免费的就让缓存执行
if (novelChapterContent.chapterInfo!.freeChapter == 1) {
handleNovel(novelChapterContent, isStart, initGetData);
} else {
//收费但是已经购买的可以让缓存执行
if (novelChapterContent.chapterInfo!.isBuy != null) {
//已经购买
if (novelChapterContent.chapterInfo!.isBuy == 1) {
handleNovel(novelChapterContent, isStart, initGetData);
}
}
}
}
}
if (mounted) {
setState(() {});
}
}
}, onNetError: (errorCode, error) {
showLoading = false;
mySetState(() {});
// showToast('$error ($errorCode)');
}),
server: Servers.businessServer);
}
//点进去需要扣费,缓存需要扣费,购买过小说后再请求一次,刷新缓存,免得进来还是先拿缓存还要扣费
reGetNovelData(int bookId, int chapter) {
if (chapter <= 0) {
return;
}
RxHttp<Response>()
..init()
..setBaseUrl(Api.BUSINESS_BASE_API)
..setPath(Api.API_BOOK_CONTENT)
..setCacheMode(CacheMode.REQUEST_FAILED_READ_CACHE)
..setJsonTransFrom((p0) => Response.fromJson(json.decoder.convert(p0)))
..setParams({
"book_id": bookId,
"chapter": chapter,
})
..call(
NetCallback(
onNetFinish: (response) {},
onCacheFinish: (response) {},
onNetError: (errorCode, error) {}),
server: Servers.businessServer);
}
//添加数据时,查找当前底部列表是否有重复数据
checkDownListData(NovelChapterContent? novelChapterContent) {
bool isHasData = false;
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
for (int i = 0; i < novelContentDownList.length; i++) {
if (novelContentDownList[i].bookId ==
novelChapterContent.chapterInfo!.bookId) {
if (novelContentDownList[i].chapter ==
novelChapterContent.chapterInfo!.chapter) {
isHasData = true;
}
}
}
}
}
return isHasData;
}
//添加数据时,查找当前顶部列表是否有重复数据
checkUpListData(NovelChapterContent? novelChapterContent) {
bool isHasData = false;
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
for (int i = 0; i < novelContentUpList.length; i++) {
if (novelContentUpList[i].bookId ==
novelChapterContent.chapterInfo!.bookId) {
if (novelContentUpList[i].chapter ==
novelChapterContent.chapterInfo!.chapter) {
isHasData = true;
}
}
}
}
}
return isHasData;
}
//处理小说扣费
//添加数据时检查列表是否够满屏,没满屏的话显示控件
handleNovel(
NovelChapterContent? novelChapterContent, bool isStart, bool? initData) {
showLoading = false;
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
if (isStart) {
//顶部列表去重
if (!novelContentUpList.contains(novelChapterContent.chapterInfo)) {
//顶部列表遍历去重
if (checkUpListData(novelChapterContent) == false) {
//底部列表contains去重
if (!novelContentDownList
.contains(novelChapterContent.chapterInfo)) {
//底部列表遍历去重,其实应该要删掉旧的数据(缓存),添加新的数据(网络)
if (checkDownListData(novelChapterContent) == false) {
//往顶部插入数据
//不免费章节
if (novelChapterContent.chapterInfo!.freeChapter == 2) {
//判断是否已购买
if (novelChapterContent.chapterInfo!.isBuy == 2) {
//已经弹出扣费弹窗了,就不再弹
if (hasShowNovelFreeDialog) {
} else {
showModalBottomSheet(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24))),
isScrollControlled: true,
enableDrag: false,
backgroundColor: CommonColors.getColorF5F8F5,
context: context,
builder: (context) =>
BuyNovelDialog(novelChapterContent))
.then((value) {
if (value != null) {
if (value == true) {
//购买了本书
//顶部列表去重
if (checkUpListData(novelChapterContent) ==
false) {
//底部列表去重
if (checkDownListData(novelChapterContent) ==
false) {
//底部列表为空的时候优先插入底部列表,不然就是空白一片了
if(novelContentDownList.isEmpty){
novelContentDownList.add(novelChapterContent.chapterInfo!);
}else{
//底部列表的高度不够屏幕高度时,优先插入数据在底部列表
if(getDownListHeight()<Global.screenHeight - paddingSize - getHeadHeight()){
novelContentDownList.insert(0,novelChapterContent.chapterInfo!);
PrintUtil.prints('优先插入数据到底部列表');
}else{
novelContentUpList.insert(
0, novelChapterContent.chapterInfo!);
}
}
novelContentDownList.sort((a, b) =>
a.chapter!.compareTo(b.chapter!));
novelContentUpList.sort((a, b) =>
a.chapter!.compareTo(b.chapter!));
startPage = startPage - 1;
}
}
//重新请求一下数据,刷新缓存数据
reGetNovelData(widget.chapterInfo?.bookId ?? 1,
startPage + 1);
//删除原来的记录,保存新的记录,查询
deleteOldUserIdAndChapter(novelChapterContent);
savaUserIdAndChapter(novelChapterContent);
checkNovelData(novelChapterContent);
mySetState(() {});
Future.delayed(const Duration(milliseconds: 100),
() {
addSeatWidget();
});
}
} else {
//没购买到章节,不做处理
}
});
}
} else {
//已购买过章节
//底部列表为空的时候优先插入底部列表,不然就是空白一片了
if(novelContentDownList.isEmpty){
novelContentDownList.add(novelChapterContent.chapterInfo!);
}else{
//底部列表的高度不够屏幕高度时,优先插入数据在底部列表
if(getDownListHeight()<Global.screenHeight - paddingSize - getHeadHeight()){
novelContentDownList.insert(0,novelChapterContent.chapterInfo!);
PrintUtil.prints('优先插入数据到底部列表');
}else{
novelContentUpList.insert(
0, novelChapterContent.chapterInfo!);
}
}
novelContentDownList
.sort((a, b) => a.chapter!.compareTo(b.chapter!));
novelContentUpList
.sort((a, b) => a.chapter!.compareTo(b.chapter!));
startPage = startPage - 1;
//删除原来的记录,保存新的记录,查询
deleteOldUserIdAndChapter(novelChapterContent);
savaUserIdAndChapter(novelChapterContent);
checkNovelData(novelChapterContent);
mySetState(() {});
Future.delayed(const Duration(milliseconds: 100), () {
addSeatWidget();
});
}
} else {
//免费章节
//底部列表为空的时候优先插入底部列表,不然就是空白一片了
if(novelContentDownList.isEmpty){
novelContentDownList.add(novelChapterContent.chapterInfo!);
}else{
//底部列表的高度不够屏幕高度时,优先插入数据在底部列表
if(getDownListHeight()<Global.screenHeight - paddingSize - getHeadHeight()){
novelContentDownList.insert(0,novelChapterContent.chapterInfo!);
PrintUtil.prints('优先插入数据到底部列表');
}else{
novelContentUpList.insert(
0, novelChapterContent.chapterInfo!);
}
}
novelContentUpList
.sort((a, b) => a.chapter!.compareTo(b.chapter!));
novelContentDownList
.sort((a, b) => a.chapter!.compareTo(b.chapter!));
startPage = startPage - 1;
//删除原来的记录,保存新的记录,查询
deleteOldUserIdAndChapter(novelChapterContent);
savaUserIdAndChapter(novelChapterContent);
checkNovelData(novelChapterContent);
mySetState(() {});
Future.delayed(const Duration(milliseconds: 100), () {
addSeatWidget();
});
}
}
}
}
}
} else {
//contains去重
if (!novelContentDownList.contains(novelChapterContent.chapterInfo)) {
//遍历去重,其实应该要删掉旧的数据(缓存),添加新的数据(网络)
if (checkDownListData(novelChapterContent) == false) {
if (!novelContentUpList
.contains(novelChapterContent.chapterInfo)) {
if (checkUpListData(novelChapterContent) == false) {
//往底部插入数据
//不免费章节
if (novelChapterContent.chapterInfo!.freeChapter == 2) {
//判断是否已购买
if (novelChapterContent.chapterInfo!.isBuy == 2) {
if (hasShowNovelFreeDialog) {
} else {
showModalBottomSheet(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24))),
isScrollControlled: true,
enableDrag: false,
backgroundColor: CommonColors.getColorF5F8F5,
context: context,
builder: (context) =>
BuyNovelDialog(novelChapterContent))
.then((value) {
if (value != null) {
if (value == true) {
//已购买小说
if (checkDownListData(novelChapterContent) ==
false) {
if (checkUpListData(novelChapterContent) ==
false) {
novelContentDownList
.add(novelChapterContent.chapterInfo!);
novelContentDownList.sort((a, b) =>
a.chapter!.compareTo(b.chapter!));
endPage = endPage + 1;
}
}
//如果列表大于屏幕,一进来就预留顶部位置
Future.delayed(const Duration(milliseconds: 100),
() {
if (getDoubleListHeight() >=
Global.screenHeight -
paddingSize -
getHeadHeight()) {
//添加数据后本身在顶部才跳转一个单位预留位置
if (_scrollController.position.pixels ==
_scrollController
.position.minScrollExtent) {
if (_scrollController.hasClients) {
_scrollController.jumpTo(_scrollController
.position.minScrollExtent +
1);
}
}
}
});
//重新请求一下数据,刷新缓存
reGetNovelData(
widget.chapterInfo?.bookId ?? 1, endPage - 1);
//删除原来的记录,保存新的记录,查询
deleteOldUserIdAndChapter(novelChapterContent);
savaUserIdAndChapter(novelChapterContent);
checkNovelData(novelChapterContent);
mySetState(() {});
Future.delayed(const Duration(milliseconds: 100),
() {
addSeatWidget();
});
}
} else {
//没购买到小说
//检查列表是否满屏
if (initData != null) {
if (initData = true) {
Future.delayed(
const Duration(milliseconds: 100), () {
addSeatWidget();
});
}
} else {
Future.delayed(const Duration(milliseconds: 100),
() {
addSeatWidget();
});
}
}
});
}
} else {
//已经购买过该章节
novelContentDownList
.add(novelChapterContent.chapterInfo!);
novelContentDownList
.sort((a, b) => a.chapter!.compareTo(b.chapter!));
endPage = endPage + 1;
if (initData != null) {
if (initData == true) {
//一进来就预留顶部位置
Future.delayed(const Duration(milliseconds: 100), () {
if (getDoubleListHeight() >=
Global.screenHeight -
paddingSize -
getHeadHeight()) {
//添加数据后本身在顶部才跳转一个单位预留位置
if (_scrollController.position.pixels ==
_scrollController.position.minScrollExtent) {
if (_scrollController.hasClients) {
_scrollController.jumpTo(_scrollController
.position.minScrollExtent +
1);
}
}
}
});
}
}
//删除原来的记录,保存新的记录,查询
deleteOldUserIdAndChapter(novelChapterContent);
savaUserIdAndChapter(novelChapterContent);
checkNovelData(novelChapterContent);
mySetState(() {});
Future.delayed(const Duration(milliseconds: 100), () {
addSeatWidget();
});
}
} else {
//免费章节
novelContentDownList.add(novelChapterContent.chapterInfo!);
novelContentDownList
.sort((a, b) => a.chapter!.compareTo(b.chapter!));
if (initData != null) {
if (initData == true) {
//一进来就预留顶部位置
Future.delayed(const Duration(milliseconds: 100), () {
if (getDoubleListHeight() >=
Global.screenHeight -
paddingSize -
getHeadHeight()) {
//添加数据后本身在顶部才跳转一个单位预留位置
if (_scrollController.position.pixels ==
_scrollController.position.minScrollExtent) {
if (_scrollController.hasClients) {
_scrollController.jumpTo(
_scrollController.position.minScrollExtent +
1);
}
}
}
});
}
}
endPage = endPage + 1;
//删除原来的记录,保存新的记录,查询
deleteOldUserIdAndChapter(novelChapterContent);
savaUserIdAndChapter(novelChapterContent);
checkNovelData(novelChapterContent);
mySetState(() {});
Future.delayed(const Duration(milliseconds: 100), () {
addSeatWidget();
});
}
}
}
}
}
}
}
}
}
//本地删除该书的旧章节
deleteOldUserIdAndChapter(NovelChapterContent? novelChapterContent) {
List<String> saveNovelDataList = [];
if (SPHelper.getNovelDataList == null) {
SPHelper.setNovelDataList([]);
if (SPHelper.getNovelDataList != null) {
saveNovelDataList = SPHelper.getNovelDataList;
for (int i = 0; i < saveNovelDataList.length; i++) {
SaveNovelData saveNovelData =
SaveNovelData.fromJson(jsonDecode(saveNovelDataList[i]));
if (saveNovelData.userId == Global.userId) {
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
if (saveNovelData.chapterInfo != null) {
if (novelChapterContent.chapterInfo!.bookId ==
saveNovelData.chapterInfo!.bookId) {
//保存有该书数据就删除
saveNovelDataList.removeAt(i);
}
}
}
}
}
}
//重新赋值
SPHelper.setNovelDataList(saveNovelDataList);
}
} else {
saveNovelDataList = SPHelper.getNovelDataList;
for (int i = 0; i < saveNovelDataList.length; i++) {
SaveNovelData saveNovelData =
SaveNovelData.fromJson(jsonDecode(saveNovelDataList[i]));
if (saveNovelData.userId == Global.userId) {
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
if (saveNovelData.chapterInfo != null) {
if (novelChapterContent.chapterInfo!.bookId ==
saveNovelData.chapterInfo!.bookId) {
//保存有该书数据就删除
saveNovelDataList.removeAt(i);
}
}
}
}
}
}
//重新赋值
SPHelper.setNovelDataList(saveNovelDataList);
}
}
//查询本地保存的小说记录,仅供自己使用
checkNovelData(NovelChapterContent? novelChapterContent) {
List<String> saveNovelDataList = [];
if (SPHelper.getNovelDataList == null) {
SPHelper.setNovelDataList([]);
if (SPHelper.getNovelDataList != null) {
saveNovelDataList = SPHelper.getNovelDataList;
for (int i = 0; i < saveNovelDataList.length; i++) {
SaveNovelData saveNovelData =
SaveNovelData.fromJson(jsonDecode(saveNovelDataList[i]));
if (saveNovelData.userId == Global.userId) {
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
if (saveNovelData.chapterInfo != null) {
if (novelChapterContent.chapterInfo!.bookId ==
saveNovelData.chapterInfo!.bookId) {}
}
}
}
}
}
//重新赋值
SPHelper.setNovelDataList(saveNovelDataList);
}
} else {
saveNovelDataList = SPHelper.getNovelDataList;
for (int i = 0; i < saveNovelDataList.length; i++) {
SaveNovelData saveNovelData =
SaveNovelData.fromJson(jsonDecode(saveNovelDataList[i]));
if (saveNovelData.userId == Global.userId) {
if (novelChapterContent != null) {
if (novelChapterContent.chapterInfo != null) {
if (saveNovelData.chapterInfo != null) {
if (novelChapterContent.chapterInfo!.bookId ==
saveNovelData.chapterInfo!.bookId) {}
}
}
}
}
}
//重新赋值
SPHelper.setNovelDataList(saveNovelDataList);
}
}
//本地添加该书的新章节记录
savaUserIdAndChapter(NovelChapterContent? novelChapterContent) {
if (novelChapterContent != null) {
List<String> saveNovelDataList = [];
//获取已经存好的数据
if (SPHelper.getNovelDataList == null) {
SPHelper.setNovelDataList([]);
if (SPHelper.getNovelDataList != null) {
saveNovelDataList = SPHelper.getNovelDataList;
SaveNovelData saveNovelData = SaveNovelData(
userId: Global.userId,
chapterInfo: novelChapterContent.chapterInfo);
saveNovelDataList.add(jsonEncode(saveNovelData.toJson()));
SPHelper.setNovelDataList(saveNovelDataList);
}
} else {
saveNovelDataList = SPHelper.getNovelDataList;
SaveNovelData sayHiData = SaveNovelData(
userId: Global.userId,
chapterInfo: novelChapterContent.chapterInfo);
saveNovelDataList.add(jsonEncode(sayHiData.toJson()));
SPHelper.setNovelDataList(saveNovelDataList);
}
}
}
//点击屏幕上边和下边
void _onTapDown(TapUpDetails details) async {
/// 上边触摸
startPosition = details.globalPosition;
//频幕高度
double layoutHeight = Global.screenHeight;
if (startPosition.dy < (layoutHeight / 2)) {
if (_scrollController.position.pixels -
_scrollController.position.minScrollExtent >=
Global.screenHeight) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.pixels -
Global.screenHeight +
(Global.topHeight + 30 + 20),
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
} else {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.minScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
}
} else {
/// 下边触摸
if (_scrollController.position.maxScrollExtent -
_scrollController.position.pixels >=
Global.screenHeight) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.pixels +
Global.screenHeight -
(Global.topHeight + 30 + 20),
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
} else {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
}
}
}
//点击占位控件
seatWidgetTap(TapUpDetails details) {
/// 上边触摸
startPosition = details.globalPosition;
//频幕高度
double layoutHeight = Global.screenHeight;
if (startPosition.dy < (layoutHeight / 2)) {
if (_scrollController.position.pixels -
_scrollController.position.minScrollExtent >=
Global.screenHeight) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.pixels -
Global.screenHeight +
(Global.topHeight + 30 + 20),
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
} else {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.minScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
}
} else {
/// 下边触摸
if (_scrollController.position.maxScrollExtent -
_scrollController.position.pixels >=
Global.screenHeight) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.pixels +
Global.screenHeight -
(Global.topHeight + 30 + 20),
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
} else {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
}
}
}
}
@override
void dispose() {
_scrollController.dispose();
if (novelDialogHasShowEvent != null) {
novelDialogHasShowEvent!.cancel();
}
super.dispose();
}
///设置占位控件的高度
addSeatWidget() {
//如果总的列表高度-前一个填充高度还是小于屏幕高度减顶部高度,重新计算填充的高度
if (getDoubleListHeight() - seatHeight <=
Global.screenHeight - paddingSize - getHeadHeight()) {
//getDoubleListHeight()就是总屏幕,可以说是包括占位符的了,如果它小于全屏-padding-头部,说明是第一次进来,没设置占位符
// 全屏减顶部等于列表高度,如果第一次进来没满屏幕,就填充
if (getDoubleListHeight() <=
Global.screenHeight - paddingSize - getHeadHeight()) {
seatHeight = Global.screenHeight -
paddingSize -
getHeadHeight() -
getDoubleListHeight() +
10;
} else {
//加上占位已经满屏幕了,但数据没满屏幕,重新计算占位高度。或者数据本身已经满屏幕了,这时就可以隐藏占位符
if (getDoubleListHeight() - seatHeight <=
Global.screenHeight - paddingSize - getHeadHeight()) {
//实际列表高度小于屏幕高度
seatHeight = Global.screenHeight -
paddingSize -
getHeadHeight() -
(getDoubleListHeight() - seatHeight) +
10;
} else {
//实际列表高度大于屏幕高度
showSeat = false;
mySetState(() {});
}
}
showSeat = true;
mySetState(() {});
} else {
//隐藏占位
showSeat = false;
mySetState(() {});
}
if (showSeat == true) {
//数据还没满屏,要让顶部有距离
if (_scrollController.hasClients) {
//添加数据后,当前位置等于0,即在顶部的时候才预留顶部位置
if (_scrollController.position.pixels ==
_scrollController.position.minScrollExtent) {
_scrollController
.jumpTo(_scrollController.position.minScrollExtent + 1);
}
}
}
}
//每次添加数据都获取两个列表的高度,小于599就填充内容
double getDoubleListHeight() {
double listHUp = 0.0;
double listHDown = 0.0;
final upListHeight = listGlobalUpKey.currentContext
?.findRenderObject()
?.semanticBounds
.size
.height;
final downListHeight = listGlobalDownKey.currentContext
?.findRenderObject()
?.semanticBounds
.size
.height;
if (upListHeight != null) {
listHUp = upListHeight;
}
if (downListHeight != null) {
listHDown = downListHeight;
}
PrintUtil.prints('列表高度123 $listHUp $listHDown');
return listHUp + listHDown;
}
//获取底部列表的高度
getDownListHeight(){
double listHDown = 0.0;
final downListHeight = listGlobalDownKey.currentContext
?.findRenderObject()
?.semanticBounds
.size
.height;
if (downListHeight != null) {
listHDown = downListHeight;
}
PrintUtil.prints('底部列表$listHDown');
return listHDown;
}
//获取头部高度
getHeadHeight() {
double height = 0.0;
//顶部高度
final containerHeight = _globalKey.currentContext?.size?.height;
if (containerHeight != null) {
height = containerHeight;
}
PrintUtil.prints('头部的距离是$height');
return height;
}
@override
Widget build(BuildContext context) {
return Material(
child: Stack(
alignment: Alignment.bottomCenter,
children: [
Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/bg_novel_read.png'),
fit: BoxFit.fill // 完全填充
)),
//列表的高度=全局高度-paddingSize20-顶部59=599
padding: EdgeInsets.only(bottom: paddingSize),
child: Column(
children: [
addHeadView(),
Expanded(
child: GestureDetector(
onTapUp: _onTapDown,
child: CustomScrollView(
controller: _scrollController,
//就是这个centerKey让两个列表区分开来,拉上一页的时候不会跳到顶部
center: centerKey,
slivers: [
SliverToBoxAdapter(
child: Column(
key: listGlobalUpKey,
mainAxisAlignment: MainAxisAlignment.start,
children: [
MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView.builder(
itemCount: novelContentUpList.length,
physics:
const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder:
(BuildContext context, int index) {
return Container(
margin: EdgeInsets.only(
left: 16.r, right: 16.r),
padding:
EdgeInsets.only(bottom: 20.r),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
children: [
//下拉加载下一页,列表拼接,显示对象里的文本字段
Container(
alignment:
Alignment.centerLeft,
padding: EdgeInsets.only(
bottom: 15.0.r,
top: 30.r),
child: Text(
novelContentUpList[index]
.title ??
'',
style: TextStyle(
color: const Color(
0xFF222222),
fontSize: 24.sp),
),
),
Text(
novelContentUpList[index]
.content ??
'',
style: TextStyle(
color: const Color(
0xFF222222),
fontSize: 20.sp,
height: 2.2,
letterSpacing: 1.5)),
],
),
);
}))
],
)),
SliverPadding(
padding: EdgeInsets.zero,
key: centerKey,
),
SliverToBoxAdapter(
child: Column(
key: listGlobalDownKey,
mainAxisAlignment: MainAxisAlignment.start,
children: [
MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView.builder(
itemCount: novelContentDownList.length,
physics:
const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder:
(BuildContext context, int index) {
return Container(
margin: EdgeInsets.only(
left: 16.r, right: 16.r),
padding:
EdgeInsets.only(bottom: 20.r),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
children: [
//下拉加载下一页,列表拼接,显示对象里的文本字段
Container(
alignment:
Alignment.centerLeft,
padding: EdgeInsets.only(
bottom: 15.0.r,
top: 30.r),
child: Text(
novelContentDownList[index]
.title ??
'',
style: TextStyle(
color: const Color(
0xFF222222),
fontSize: 24.sp),
),
),
Text(
novelContentDownList[index]
.content ??
'',
style: TextStyle(
color: const Color(
0xFF222222),
fontSize: 20.sp,
height: 2.2,
letterSpacing: 1.5)),
],
),
);
}))
],
)),
if (showSeat)
SliverToBoxAdapter(
child: GestureDetector(
onTapUp: seatWidgetTap,
child: Container(
height: seatHeight,
color: Colors.transparent,
),
),
)
//用SliverList获取不到列表的高度,所以用SliverToBoxAdapter
// Container(
// key: listGlobalUpKey,
// child: SliverList(
// delegate: SliverChildBuilderDelegate(
// (BuildContext context, int index) {
// return Container(
// color: Colors.blue,
// margin: EdgeInsets.only(
// left: 16.r, right: 16.r),
// padding: EdgeInsets.only(bottom: 20.r),
// child: Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// mainAxisAlignment:
// MainAxisAlignment.start,
// children: [
// //下拉加载下一页,列表拼接,显示对象里的文本字段
// Container(
// alignment: Alignment.centerLeft,
// padding: EdgeInsets.only(
// bottom: 15.0.r, top: 30.r),
// child: Text(
// novelContentUpList[index].title ??
// '',
// style: TextStyle(
// color:
// const Color(0xFF222222),
// fontSize: 24.sp),
// ),
// ),
// Text(
// novelContentUpList[index]
// .content ??
// '',
// style: TextStyle(
// color:
// const Color(0xFF222222),
// fontSize: 20.sp,
// height: 2.2,
// letterSpacing: 1.5)),
// ],
// ),
// );
// },
// childCount: novelContentUpList.length,
// ),
// ),
// ),
// Container(
// // key: listGlobalDownKey,
// child: SliverList(
// delegate: SliverChildBuilderDelegate(
// (BuildContext context, int index) {
// return Container(
// color: Colors.red,
// margin: EdgeInsets.only(
// left: 16.r, right: 16.r),
// padding: EdgeInsets.only(bottom: 20.r),
// child: Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// mainAxisAlignment:
// MainAxisAlignment.start,
// children: [
// //下拉加载下一页,列表拼接,显示对象里的文本字段
// Container(
// alignment: Alignment.centerLeft,
// padding: EdgeInsets.only(
// bottom: 15.0.r, top: 30.r),
// child: Text(
// novelContentDownList[index]
// .title ??
// '',
// style: TextStyle(
// color:
// const Color(0xFF222222),
// fontSize: 24.sp),
// ),
// ),
// Text(
// novelContentDownList[index]
// .content ??
// '',
// style: TextStyle(
// color:
// const Color(0xFF222222),
// fontSize: 20.sp,
// height: 2.2,
// letterSpacing: 1.5)),
// ],
// ),
// );
// },
// childCount: novelContentDownList.length,
// ),
// ),
// ),
],
),
),
),
],
)),
showLoading
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.only(bottom: 3.r),
alignment: Alignment.center,
width: 16.r,
height: 16.r,
child: const LoadingIndicator(
indicatorType: Indicator.circleStrokeSpin,
/// Required, The loading type of the widget
colors: [Color(0xFFFF4E68)],
/// Optional, The color collections
strokeWidth: 2,
/// Optional, The stroke of the line, only applicable to widget which contains line
backgroundColor: Colors.transparent,
/// Optional, Background of the widget
pathBackgroundColor: Color(0xCCFFFFFF)
/// Optional, the stroke backgroundColor
),
),
],
)
: Container()
],
),
);
}
Widget addHeadView() {
return Container(
key: _globalKey,
padding:
EdgeInsets.only(top: Global.topHeight + 10, left: 12.r, right: 5.r),
child: Row(children: [
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: const Image(
image: AssetImage('assets/images/icon_back@2x.png'),
width: 24,
height: 25,
color: Colors.black,
),
),
LimitedBox(
maxWidth: Global.screenWidth - 50,
child: Text(
"${widget.novelName}",
style: const TextStyle(color: Color(0xFF222222), fontSize: 16),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
)
]));
}
}
关键代码是
CustomScrollView(
controller: _scrollController,
//就是这个centerKey让两个列表区分开来,拉上一页的时候不会跳到顶部
center: centerKey,
slivers: [
SliverToBoxAdapter(
child: Column(
key: listGlobalUpKey,
mainAxisAlignment: MainAxisAlignment.start,
children: [
MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView.builder(
itemCount: novelContentUpList.length,
physics:
const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder:
(BuildContext context, int index) {
return Container(
margin: EdgeInsets.only(
left: 16.r, right: 16.r),
padding:
EdgeInsets.only(bottom: 20.r),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
children: [
//下拉加载下一页,列表拼接,显示对象里的文本字段
Container(
alignment:
Alignment.centerLeft,
padding: EdgeInsets.only(
bottom: 15.0.r,
top: 30.r),
child: Text(
novelContentUpList[index]
.title ??
'',
style: TextStyle(
color: const Color(
0xFF222222),
fontSize: 24.sp),
),
),
Text(
novelContentUpList[index]
.content ??
'',
style: TextStyle(
color: const Color(
0xFF222222),
fontSize: 20.sp,
height: 2.2,
letterSpacing: 1.5)),
],
),
);
}))
],
)),
SliverPadding(
padding: EdgeInsets.zero,
key: centerKey,
),
SliverToBoxAdapter(
child: Column(
key: listGlobalDownKey,
mainAxisAlignment: MainAxisAlignment.start,
children: [
MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView.builder(
itemCount: novelContentDownList.length,
physics:
const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder:
(BuildContext context, int index) {
return Container(
margin: EdgeInsets.only(
left: 16.r, right: 16.r),
padding:
EdgeInsets.only(bottom: 20.r),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
children: [
//下拉加载下一页,列表拼接,显示对象里的文本字段
Container(
alignment:
Alignment.centerLeft,
padding: EdgeInsets.only(
bottom: 15.0.r,
top: 30.r),
child: Text(
novelContentDownList[index]
.title ??
'',
style: TextStyle(
color: const Color(
0xFF222222),
fontSize: 24.sp),
),
),
Text(
novelContentDownList[index]
.content ??
'',
style: TextStyle(
color: const Color(
0xFF222222),
fontSize: 20.sp,
height: 2.2,
letterSpacing: 1.5)),
],
),
);
}))
],
)),
if (showSeat)
SliverToBoxAdapter(
child: GestureDetector(
onTapUp: seatWidgetTap,
child: Container(
height: seatHeight,
color: Colors.transparent,
),
),
)
//用SliverList获取不到列表的高度(列表高度不够满屏,滑动监听不了),所以用SliverToBoxAdapter
],
),