mirror of
https://github.com/nrop19/weiman_app.git
synced 2025-08-03 15:22:47 +08:00
1.0.5
This commit is contained in:
parent
582d231063
commit
b86adbb991
@ -11,8 +11,9 @@ class ActivityBook extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class BookState extends State<ActivityBook> {
|
class BookState extends State<ActivityBook> {
|
||||||
|
static BoxDecoration _border;
|
||||||
final GlobalKey<PullToRefreshNotificationState> _refresh = GlobalKey();
|
final GlobalKey<PullToRefreshNotificationState> _refresh = GlobalKey();
|
||||||
GlobalKey<NestedScrollViewState> _key = GlobalKey<NestedScrollViewState>();
|
ScrollController _scrollController;
|
||||||
|
|
||||||
bool _reverse = false;
|
bool _reverse = false;
|
||||||
bool isFavorite = false;
|
bool isFavorite = false;
|
||||||
@ -27,6 +28,13 @@ class BookState extends State<ActivityBook> {
|
|||||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||||
_refresh.currentState.show();
|
_refresh.currentState.show();
|
||||||
});
|
});
|
||||||
|
_scrollController = ScrollController();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> loadBook() async {
|
Future<bool> loadBook() async {
|
||||||
@ -64,7 +72,7 @@ class BookState extends State<ActivityBook> {
|
|||||||
final history = book.chapters
|
final history = book.chapters
|
||||||
.firstWhere((chapter) => chapter.cid == book.history.cid);
|
.firstWhere((chapter) => chapter.cid == book.history.cid);
|
||||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||||
_key.currentState.currentInnerPosition.animateTo(
|
_scrollController.animateTo(
|
||||||
WidgetChapter.height * chapters.indexOf(history).toDouble(),
|
WidgetChapter.height * chapters.indexOf(history).toDouble(),
|
||||||
duration: Duration(milliseconds: 500),
|
duration: Duration(milliseconds: 500),
|
||||||
curve: Curves.linear);
|
curve: Curves.linear);
|
||||||
@ -93,70 +101,6 @@ class BookState extends State<ActivityBook> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _headerBuilder(BuildContext context, bool innerBoxIsScrolled) {
|
|
||||||
Color color = isFavorite ? Colors.red : Colors.white;
|
|
||||||
IconData icon = isFavorite ? Icons.favorite : Icons.favorite_border;
|
|
||||||
final book = this.book ?? widget.book;
|
|
||||||
return <Widget>[
|
|
||||||
SliverAppBar(
|
|
||||||
floating: true,
|
|
||||||
pinned: true,
|
|
||||||
snap: false,
|
|
||||||
title: Text(widget.book.name),
|
|
||||||
expandedHeight: 200,
|
|
||||||
actions: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
onPressed: _sort,
|
|
||||||
icon: Icon(_reverse
|
|
||||||
? FontAwesomeIcons.sortNumericDown
|
|
||||||
: FontAwesomeIcons.sortNumericDownAlt)),
|
|
||||||
IconButton(onPressed: favoriteBook, icon: Icon(icon, color: color))
|
|
||||||
],
|
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
|
||||||
background: SafeArea(
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Container(
|
|
||||||
margin:
|
|
||||||
EdgeInsets.only(top: 50, left: 20, right: 10, bottom: 20),
|
|
||||||
height: 160,
|
|
||||||
child: Hero(
|
|
||||||
tag: widget.heroTag,
|
|
||||||
child: Image.network(
|
|
||||||
widget.book.avatar,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.only(top: 50, right: 20),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
'作者:' + (book.author ?? ''),
|
|
||||||
style: TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.only(top: 10),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'简介:\n' + (book.description ?? ''),
|
|
||||||
softWrap: true,
|
|
||||||
style: TextStyle(color: Colors.white, height: 1.2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> chapterWidgets() {
|
List<Widget> chapterWidgets() {
|
||||||
final book = this.book ?? widget.book;
|
final book = this.book ?? widget.book;
|
||||||
List<Widget> list = [];
|
List<Widget> list = [];
|
||||||
@ -175,6 +119,16 @@ class BookState extends State<ActivityBook> {
|
|||||||
final book = this.book ?? widget.book;
|
final book = this.book ?? widget.book;
|
||||||
final chapter = chapters[index];
|
final chapter = chapters[index];
|
||||||
final isRead = chapter.cid == book.history?.cid;
|
final isRead = chapter.cid == book.history?.cid;
|
||||||
|
if (index < chapters.length - 1) {
|
||||||
|
return DecoratedBox(
|
||||||
|
decoration: _border,
|
||||||
|
child: WidgetChapter(
|
||||||
|
chapter: chapter,
|
||||||
|
onTap: _openChapter,
|
||||||
|
read: isRead,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
return WidgetChapter(
|
return WidgetChapter(
|
||||||
chapter: chapter,
|
chapter: chapter,
|
||||||
onTap: _openChapter,
|
onTap: _openChapter,
|
||||||
@ -184,6 +138,10 @@ class BookState extends State<ActivityBook> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (_border == null)
|
||||||
|
_border = BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: Divider.createBorderSide(context, color: Colors.grey)));
|
||||||
Color color = isFavorite ? Colors.red : Colors.white;
|
Color color = isFavorite ? Colors.red : Colors.white;
|
||||||
IconData icon = isFavorite ? Icons.favorite : Icons.favorite_border;
|
IconData icon = isFavorite ? Icons.favorite : Icons.favorite_border;
|
||||||
final book = this.book ?? widget.book;
|
final book = this.book ?? widget.book;
|
||||||
@ -192,140 +150,79 @@ class BookState extends State<ActivityBook> {
|
|||||||
key: _refresh,
|
key: _refresh,
|
||||||
onRefresh: loadBook,
|
onRefresh: loadBook,
|
||||||
maxDragOffset: kToolbarHeight * 2,
|
maxDragOffset: kToolbarHeight * 2,
|
||||||
child: NestedScrollView(
|
child: CustomScrollView(
|
||||||
key: _key,
|
controller: _scrollController,
|
||||||
headerSliverBuilder: (_, __) => [],
|
slivers: [
|
||||||
physics: AlwaysScrollableClampingScrollPhysics(),
|
SliverAppBar(
|
||||||
body: CustomScrollView(
|
floating: true,
|
||||||
physics: AlwaysScrollableClampingScrollPhysics(),
|
pinned: true,
|
||||||
slivers: [
|
title: Text(widget.book.name),
|
||||||
SliverAppBar(
|
expandedHeight: 200,
|
||||||
floating: true,
|
actions: <Widget>[
|
||||||
pinned: false,
|
IconButton(
|
||||||
title: Text(widget.book.name),
|
onPressed: _sort,
|
||||||
expandedHeight: 200,
|
icon: Icon(_reverse
|
||||||
actions: <Widget>[
|
? FontAwesomeIcons.sortNumericDown
|
||||||
IconButton(
|
: FontAwesomeIcons.sortNumericDownAlt)),
|
||||||
onPressed: _sort,
|
IconButton(
|
||||||
icon: Icon(_reverse
|
onPressed: favoriteBook, icon: Icon(icon, color: color))
|
||||||
? FontAwesomeIcons.sortNumericDown
|
],
|
||||||
: FontAwesomeIcons.sortNumericDownAlt)),
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
IconButton(
|
background: SafeArea(
|
||||||
onPressed: favoriteBook, icon: Icon(icon, color: color))
|
child: Row(
|
||||||
],
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
children: <Widget>[
|
||||||
background: SafeArea(
|
Container(
|
||||||
child: Row(
|
margin: EdgeInsets.only(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
top: 50, left: 20, right: 10, bottom: 20),
|
||||||
children: <Widget>[
|
height: 160,
|
||||||
Container(
|
child: Hero(
|
||||||
margin: EdgeInsets.only(
|
tag: widget.heroTag,
|
||||||
top: 50, left: 20, right: 10, bottom: 20),
|
child:
|
||||||
height: 160,
|
Image(image: NetworkImageSSL(widget.book.avatar)),
|
||||||
child: Hero(
|
|
||||||
tag: widget.heroTag,
|
|
||||||
child: Image.network(
|
|
||||||
widget.book.avatar,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
child: Container(
|
Expanded(
|
||||||
padding: EdgeInsets.only(top: 50, right: 20),
|
child: Container(
|
||||||
child: Column(
|
padding: EdgeInsets.only(top: 50, right: 20),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: <Widget>[
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Text(
|
children: <Widget>[
|
||||||
'作者:' + (book.author ?? ''),
|
Text(
|
||||||
style: TextStyle(color: Colors.white),
|
'作者:' + (book.author ?? ''),
|
||||||
),
|
style: TextStyle(color: Colors.white),
|
||||||
Container(
|
),
|
||||||
margin: EdgeInsets.only(top: 10),
|
Container(
|
||||||
),
|
margin: EdgeInsets.only(top: 10),
|
||||||
Text(
|
),
|
||||||
'简介:\n' + (book.description ?? ''),
|
Text(
|
||||||
softWrap: true,
|
'简介:\n' + (book.description ?? ''),
|
||||||
style:
|
softWrap: true,
|
||||||
TextStyle(color: Colors.white, height: 1.2),
|
style:
|
||||||
),
|
TextStyle(color: Colors.white, height: 1.2),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
)),
|
),
|
||||||
],
|
)),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PullToRefreshContainer((info) => SliverPullToRefreshHeader(
|
),
|
||||||
info: info,
|
PullToRefreshContainer((info) => SliverPullToRefreshHeader(
|
||||||
onTap: () => _refresh.currentState
|
info: info,
|
||||||
.show(notificationDragOffset: kToolbarHeight * 2),
|
onTap: () => _refresh.currentState
|
||||||
)),
|
.show(notificationDragOffset: kToolbarHeight * 2),
|
||||||
NestedScrollViewInnerScrollPositionKeyWidget(
|
)),
|
||||||
Key('0'),
|
SliverList(
|
||||||
SliverList(
|
delegate: SliverChildBuilderDelegate(
|
||||||
delegate: SliverChildBuilderDelegate(
|
buildChapter,
|
||||||
buildChapter,
|
childCount: book.chapters.length,
|
||||||
childCount: book.chapters.length,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget build1(BuildContext context) {
|
|
||||||
final double statusBarHeight = MediaQuery.of(context).padding.top;
|
|
||||||
var pinnedHeaderHeight =
|
|
||||||
//statusBar height
|
|
||||||
statusBarHeight +
|
|
||||||
//pinned SliverAppBar height in header
|
|
||||||
kToolbarHeight;
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: NestedScrollViewRefreshIndicator(
|
|
||||||
key: _refresh,
|
|
||||||
onRefresh: loadBook,
|
|
||||||
child: NestedScrollView(
|
|
||||||
key: _key,
|
|
||||||
pinnedHeaderSliverHeightBuilder: () => pinnedHeaderHeight,
|
|
||||||
headerSliverBuilder: _headerBuilder,
|
|
||||||
body: LayoutBuilder(
|
|
||||||
builder: (_, __) {
|
|
||||||
if (isLoading)
|
|
||||||
return Container();
|
|
||||||
else if (isSuccess) {
|
|
||||||
return ListView(
|
|
||||||
children: ListTile.divideTiles(
|
|
||||||
context: context,
|
|
||||||
color: Colors.grey,
|
|
||||||
tiles: chapterWidgets())
|
|
||||||
.toList());
|
|
||||||
}
|
|
||||||
return Container(
|
|
||||||
constraints: BoxConstraints.expand(),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
'读取失败,下拉刷新\n如果多次失败,请检查网络',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
onPressed: () {
|
|
||||||
_key.currentState.currentInnerPosition.animateTo(0,
|
|
||||||
duration: Duration(milliseconds: 100), curve: Curves.linear);
|
|
||||||
},
|
|
||||||
child: Icon(FontAwesomeIcons.angleDoubleUp),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -117,6 +117,12 @@ class _ChapterDrawer extends State<ChapterDrawer> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void updateRead() {
|
void updateRead() {
|
||||||
final readChapter = widget.book.chapters
|
final readChapter = widget.book.chapters
|
||||||
.firstWhere((chapter) => widget.book.history?.cid == chapter.cid);
|
.firstWhere((chapter) => widget.book.history?.cid == chapter.cid);
|
||||||
@ -134,12 +140,6 @@ class _ChapterDrawer extends State<ChapterDrawer> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_controller.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Drawer(
|
return Drawer(
|
||||||
@ -183,6 +183,9 @@ class ChapterContentView extends StatefulWidget {
|
|||||||
class _ChapterContentView extends State<ChapterContentView> {
|
class _ChapterContentView extends State<ChapterContentView> {
|
||||||
final GlobalKey<PullToRefreshNotificationState> _refresh = GlobalKey();
|
final GlobalKey<PullToRefreshNotificationState> _refresh = GlobalKey();
|
||||||
final List<String> images = [];
|
final List<String> images = [];
|
||||||
|
TextStyle _style = TextStyle(color: Colors.white);
|
||||||
|
BoxDecoration _decoration =
|
||||||
|
BoxDecoration(color: Colors.black.withOpacity(0.4));
|
||||||
|
|
||||||
int chapterIndex = -1;
|
int chapterIndex = -1;
|
||||||
bool hasNextChapter = false;
|
bool hasNextChapter = false;
|
||||||
@ -223,6 +226,30 @@ class _ChapterContentView extends State<ChapterContentView> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final List<Widget> list = [];
|
||||||
|
for (var i = 0; i < images.length; i++) {
|
||||||
|
list.add(SliverStickyHeader(
|
||||||
|
overlapsContent: true,
|
||||||
|
header: SafeArea(
|
||||||
|
top: true,
|
||||||
|
bottom: false,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(5),
|
||||||
|
decoration: _decoration,
|
||||||
|
child: Text(
|
||||||
|
'${i + 1} / ${images.length}',
|
||||||
|
style: _style,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
sliver:
|
||||||
|
SliverToBoxAdapter(child: Image(image: NetworkImageSSL(images[i]))),
|
||||||
|
));
|
||||||
|
}
|
||||||
return PullToRefreshNotification(
|
return PullToRefreshNotification(
|
||||||
key: _refresh,
|
key: _refresh,
|
||||||
onRefresh: fetchImages,
|
onRefresh: fetchImages,
|
||||||
@ -236,16 +263,14 @@ class _ChapterContentView extends State<ChapterContentView> {
|
|||||||
floating: true,
|
floating: true,
|
||||||
actions: widget.actions,
|
actions: widget.actions,
|
||||||
),
|
),
|
||||||
PullToRefreshContainer((info) => SliverPullToRefreshHeader(
|
PullToRefreshContainer(
|
||||||
info: info,
|
(info) => SliverPullToRefreshHeader(
|
||||||
onTap: () => _refresh.currentState
|
info: info,
|
||||||
.show(notificationDragOffset: kToolbarHeight * 2),
|
onTap: () => _refresh.currentState
|
||||||
)),
|
.show(notificationDragOffset: kToolbarHeight * 2),
|
||||||
SliverList(
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
|
||||||
(ctx, i) => Image.network(images[i]),
|
|
||||||
childCount: images.length),
|
|
||||||
),
|
),
|
||||||
|
...list,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -25,6 +25,7 @@ class HomeState extends State<ActivityHome> {
|
|||||||
|
|
||||||
/// 提前检查一次藏书的更新情况
|
/// 提前检查一次藏书的更新情况
|
||||||
SchedulerBinding.instance.addPostFrameCallback((_) async {
|
SchedulerBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
autoSwitchTheme();
|
||||||
_FavoriteList.getBooks();
|
_FavoriteList.getBooks();
|
||||||
await _FavoriteList.checkNews();
|
await _FavoriteList.checkNews();
|
||||||
final updated = _FavoriteList.hasNews.values
|
final updated = _FavoriteList.hasNews.values
|
||||||
@ -39,6 +40,14 @@ class HomeState extends State<ActivityHome> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void autoSwitchTheme() async {
|
||||||
|
final isDark = await DynamicTheme.of(context).loadBrightness();
|
||||||
|
final nowIsDark = DynamicTheme.of(context).brightness == Brightness.dark;
|
||||||
|
if (isDark != nowIsDark)
|
||||||
|
DynamicTheme.of(context)
|
||||||
|
.setBrightness(isDark ? Brightness.dark : Brightness.light);
|
||||||
|
}
|
||||||
|
|
||||||
void gotoSearch() {
|
void gotoSearch() {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
@ -66,9 +75,8 @@ class HomeState extends State<ActivityHome> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var media = MediaQuery.of(context);
|
final media = MediaQuery.of(context);
|
||||||
var width = media.size.width;
|
final width = (media.size.width * 0.8).roundToDouble();
|
||||||
width = width * .8;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
@ -183,10 +191,12 @@ class HomeState extends State<ActivityHome> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Quick(
|
Center(
|
||||||
key: _quickState,
|
child: Quick(
|
||||||
width: width,
|
key: _quickState,
|
||||||
draggableModeChanged: _draggableModeChanged,
|
width: width,
|
||||||
|
draggableModeChanged: _draggableModeChanged,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
margin: EdgeInsets.only(bottom: 10),
|
margin: EdgeInsets.only(bottom: 10),
|
||||||
@ -238,21 +248,3 @@ class HomeState extends State<ActivityHome> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterable<Widget> favoriteTiles(context, Iterable<Book> books,
|
|
||||||
{void Function(Book book) onTap}) {
|
|
||||||
return books.map((book) => ListTile(
|
|
||||||
onTap: () {
|
|
||||||
onTap(book);
|
|
||||||
},
|
|
||||||
title: Text(book.name),
|
|
||||||
leading: Image.network(book.avatar),
|
|
||||||
subtitle: Text(
|
|
||||||
'作者:' + book.author,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
@ -69,7 +69,12 @@ class SearchState extends State<Search> {
|
|||||||
},
|
},
|
||||||
child: TextField(
|
child: TextField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: '搜索书名', prefixIcon: Icon(Icons.search)),
|
hintText: '搜索书名',
|
||||||
|
prefixIcon: IconButton(
|
||||||
|
onPressed: startSearch,
|
||||||
|
icon: Icon(Icons.search),
|
||||||
|
),
|
||||||
|
),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
part of '../main.dart';
|
part of '../main.dart';
|
||||||
|
|
||||||
const domain = '';
|
const domain = '';
|
||||||
final host = Uri.parse(domain).host;
|
|
||||||
|
|
||||||
class UserAgentClient extends http.BaseClient {
|
class UserAgentClient extends http.BaseClient {
|
||||||
final String userAgent;
|
final String userAgent;
|
||||||
|
124
lib/classes/networkImageSSL.dart
Normal file
124
lib/classes/networkImageSSL.dart
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/painting.dart' as image_provider;
|
||||||
|
|
||||||
|
/// The dart:io implementation of [image_provider.NetworkImage].
|
||||||
|
class NetworkImageSSL
|
||||||
|
extends image_provider.ImageProvider<image_provider.NetworkImage>
|
||||||
|
implements image_provider.NetworkImage {
|
||||||
|
/// Creates an object that fetches the image at the given URL.
|
||||||
|
///
|
||||||
|
/// The arguments [url] and [scale] must not be null.
|
||||||
|
const NetworkImageSSL(this.url, {this.scale = 1.0, this.headers})
|
||||||
|
: assert(url != null),
|
||||||
|
assert(scale != null);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String url;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final double scale;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final Map<String, String> headers;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<NetworkImageSSL> obtainKey(
|
||||||
|
image_provider.ImageConfiguration configuration) {
|
||||||
|
return SynchronousFuture<NetworkImageSSL>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
image_provider.ImageStreamCompleter load(
|
||||||
|
image_provider.NetworkImage key, image_provider.DecoderCallback decode) {
|
||||||
|
// Ownership of this controller is handed off to [_loadAsync]; it is that
|
||||||
|
// method's responsibility to close the controller's stream when the image
|
||||||
|
// has been loaded or an error is thrown.
|
||||||
|
final StreamController<image_provider.ImageChunkEvent> chunkEvents =
|
||||||
|
StreamController<image_provider.ImageChunkEvent>();
|
||||||
|
|
||||||
|
return image_provider.MultiFrameImageStreamCompleter(
|
||||||
|
codec: _loadAsync(key, chunkEvents, decode),
|
||||||
|
chunkEvents: chunkEvents.stream,
|
||||||
|
scale: key.scale,
|
||||||
|
informationCollector: () {
|
||||||
|
return <DiagnosticsNode>[
|
||||||
|
DiagnosticsProperty<image_provider.ImageProvider>(
|
||||||
|
'Image provider', this),
|
||||||
|
DiagnosticsProperty<image_provider.NetworkImage>('Image key', key),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not access this field directly; use [_httpClient] instead.
|
||||||
|
// We set `autoUncompress` to false to ensure that we can trust the value of
|
||||||
|
// the `Content-Length` HTTP header. We automatically uncompress the content
|
||||||
|
// in our call to [consolidateHttpClientResponseBytes].
|
||||||
|
static final HttpClient _sharedHttpClient = HttpClient()
|
||||||
|
..autoUncompress = false
|
||||||
|
..badCertificateCallback = (_, __, ___) => true;
|
||||||
|
|
||||||
|
static HttpClient get _httpClient {
|
||||||
|
HttpClient client = _sharedHttpClient;
|
||||||
|
assert(() {
|
||||||
|
if (image_provider.debugNetworkImageHttpClientProvider != null)
|
||||||
|
client = image_provider.debugNetworkImageHttpClientProvider();
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ui.Codec> _loadAsync(
|
||||||
|
NetworkImageSSL key,
|
||||||
|
StreamController<image_provider.ImageChunkEvent> chunkEvents,
|
||||||
|
image_provider.DecoderCallback decode,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
assert(key == this);
|
||||||
|
|
||||||
|
final Uri resolved = Uri.base.resolve(key.url);
|
||||||
|
final HttpClientRequest request = await _httpClient.getUrl(resolved);
|
||||||
|
headers?.forEach((String name, String value) {
|
||||||
|
request.headers.add(name, value);
|
||||||
|
});
|
||||||
|
final HttpClientResponse response = await request.close();
|
||||||
|
if (response.statusCode != HttpStatus.ok)
|
||||||
|
throw image_provider.NetworkImageLoadException(
|
||||||
|
statusCode: response.statusCode, uri: resolved);
|
||||||
|
|
||||||
|
final Uint8List bytes = await consolidateHttpClientResponseBytes(
|
||||||
|
response,
|
||||||
|
onBytesReceived: (int cumulative, int total) {
|
||||||
|
chunkEvents.add(image_provider.ImageChunkEvent(
|
||||||
|
cumulativeBytesLoaded: cumulative,
|
||||||
|
expectedTotalBytes: total,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (bytes.lengthInBytes == 0)
|
||||||
|
throw Exception('NetworkImage is an empty file: $resolved');
|
||||||
|
|
||||||
|
return decode(bytes);
|
||||||
|
} finally {
|
||||||
|
chunkEvents.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
if (other.runtimeType != runtimeType) return false;
|
||||||
|
final NetworkImageSSL typedOther = other;
|
||||||
|
return url == typedOther.url && scale == typedOther.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => ui.hashValues(url, scale);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => '$runtimeType("$url", scale: $scale)';
|
||||||
|
}
|
@ -8,7 +8,6 @@ import 'package:async/async.dart';
|
|||||||
import 'package:draggable_container/draggable_container.dart';
|
import 'package:draggable_container/draggable_container.dart';
|
||||||
import 'package:dynamic_theme/dynamic_theme.dart';
|
import 'package:dynamic_theme/dynamic_theme.dart';
|
||||||
import 'package:encrypt/encrypt.dart' as encrypt;
|
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||||
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
|
||||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||||
import 'package:firebase_analytics/observer.dart';
|
import 'package:firebase_analytics/observer.dart';
|
||||||
import 'package:flutter/material.dart' hide NestedScrollView;
|
import 'package:flutter/material.dart' hide NestedScrollView;
|
||||||
@ -26,6 +25,7 @@ import 'package:pull_to_refresh_notification/pull_to_refresh_notification.dart'
|
|||||||
hide CircularProgressIndicator;
|
hide CircularProgressIndicator;
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import 'classes/networkImageSSL.dart';
|
||||||
|
|
||||||
part './activities/book.dart';
|
part './activities/book.dart';
|
||||||
|
|
||||||
@ -85,15 +85,6 @@ void main() async {
|
|||||||
UserAgentClient.init(
|
UserAgentClient.init(
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36');
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36');
|
||||||
runApp(Main(packageInfo: packageInfo));
|
runApp(Main(packageInfo: packageInfo));
|
||||||
// runApp(MaterialApp(
|
|
||||||
// title: '微漫',
|
|
||||||
// theme: ThemeData.light(),
|
|
||||||
// darkTheme: ThemeData.dark(),
|
|
||||||
// themeMode: ThemeMode.system,
|
|
||||||
// debugShowCheckedModeBanner: false,
|
|
||||||
// navigatorObservers: [observer],
|
|
||||||
// home: ActivityHome(packageInfo),
|
|
||||||
// ));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Main extends StatefulWidget {
|
class Main extends StatefulWidget {
|
||||||
@ -106,24 +97,10 @@ class Main extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _Main extends State<Main> with WidgetsBindingObserver {
|
class _Main extends State<Main> with WidgetsBindingObserver {
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DynamicTheme(
|
return DynamicTheme(
|
||||||
defaultBrightness: ThemeMode.system == ThemeMode.light
|
defaultBrightness: Brightness.dark,
|
||||||
? Brightness.light
|
|
||||||
: Brightness.dark,
|
|
||||||
data: (brightness) => new ThemeData(
|
data: (brightness) => new ThemeData(
|
||||||
brightness: brightness,
|
brightness: brightness,
|
||||||
),
|
),
|
||||||
@ -148,10 +125,4 @@ class _Main extends State<Main> with WidgetsBindingObserver {
|
|||||||
// home: ActivityHome(widget.packageInfo),
|
// home: ActivityHome(widget.packageInfo),
|
||||||
// );
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangePlatformBrightness() {
|
|
||||||
print('改变亮度');
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,8 @@ class WidgetBook extends StatelessWidget {
|
|||||||
dense: true,
|
dense: true,
|
||||||
leading: Hero(
|
leading: Hero(
|
||||||
tag: 'bookAvatar${book.aid}',
|
tag: 'bookAvatar${book.aid}',
|
||||||
child: Image.network(
|
child: Image(image:NetworkImageSSL(
|
||||||
book.avatar,
|
book.avatar),
|
||||||
height: 200,
|
height: 200,
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
)),
|
)),
|
||||||
@ -48,7 +48,7 @@ class WidgetBook extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class WidgetChapter extends StatelessWidget {
|
class WidgetChapter extends StatelessWidget {
|
||||||
static final double height = 56;
|
static final double height = kToolbarHeight;
|
||||||
final Chapter chapter;
|
final Chapter chapter;
|
||||||
final Function(Chapter) onTap;
|
final Function(Chapter) onTap;
|
||||||
final bool read;
|
final bool read;
|
||||||
@ -83,8 +83,8 @@ class WidgetChapter extends StatelessWidget {
|
|||||||
softWrap: true,
|
softWrap: true,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
),
|
),
|
||||||
leading: Image.network(
|
leading: Image(image:NetworkImageSSL(
|
||||||
chapter.avatar,
|
chapter.avatar),
|
||||||
fit: BoxFit.fitWidth,
|
fit: BoxFit.fitWidth,
|
||||||
width: 100,
|
width: 100,
|
||||||
),
|
),
|
||||||
@ -106,8 +106,8 @@ class WidgetHistory extends StatelessWidget {
|
|||||||
if (onTap != null) onTap(book);
|
if (onTap != null) onTap(book);
|
||||||
},
|
},
|
||||||
title: Text(book.name),
|
title: Text(book.name),
|
||||||
leading: Image.network(
|
leading: Image(image:NetworkImageSSL(
|
||||||
book.avatar,
|
book.avatar),
|
||||||
fit: BoxFit.fitHeight,
|
fit: BoxFit.fitHeight,
|
||||||
),
|
),
|
||||||
subtitle: Text(book.history.cname),
|
subtitle: Text(book.history.cname),
|
||||||
@ -173,7 +173,7 @@ class _WidgetBookCheckNew extends State<WidgetBookCheckNew> {
|
|||||||
openBook(context, widget.book, 'checkBook${widget.book.aid}'),
|
openBook(context, widget.book, 'checkBook${widget.book.aid}'),
|
||||||
leading: Hero(
|
leading: Hero(
|
||||||
tag: 'checkBook${widget.book.aid}',
|
tag: 'checkBook${widget.book.aid}',
|
||||||
child: Image.network(widget.book.avatar),
|
child: Image(image:NetworkImageSSL(widget.book.avatar)),
|
||||||
),
|
),
|
||||||
dense: true,
|
dense: true,
|
||||||
isThreeLine: true,
|
isThreeLine: true,
|
||||||
|
@ -174,7 +174,7 @@ class FBookItem extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () => onTap(book),
|
onTap: () => onTap(book),
|
||||||
leading: Hero(tag: 'fb ${book.aid}', child: Image.network(book.avatar)),
|
leading: Hero(tag: 'fb ${book.aid}', child: Image(image:NetworkImageSSL(book.avatar))),
|
||||||
title: Text(book.name, style: Theme.of(context).textTheme.body1),
|
title: Text(book.name, style: Theme.of(context).textTheme.body1),
|
||||||
subtitle: RichText(text: subtitle),
|
subtitle: RichText(text: subtitle),
|
||||||
);
|
);
|
||||||
|
@ -21,7 +21,7 @@ class QuickBook extends DraggableItem {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Hero(
|
Hero(
|
||||||
tag: '$heroTag ${book.aid}',
|
tag: '$heroTag ${book.aid}',
|
||||||
child: Image.network(book.avatar),
|
child: Image(image: NetworkImageSSL(book.avatar)),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 0,
|
left: 0,
|
||||||
@ -79,7 +79,7 @@ class QuickState extends State<Quick> {
|
|||||||
.where((book) => !id.contains(book.aid))
|
.where((book) => !id.contains(book.aid))
|
||||||
.map((book) => ListTile(
|
.map((book) => ListTile(
|
||||||
title: Text(book.name),
|
title: Text(book.name),
|
||||||
leading: Image.network(book.avatar),
|
leading: Image(image: NetworkImageSSL(book.avatar)),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context, book);
|
Navigator.pop(context, book);
|
||||||
},
|
},
|
||||||
@ -144,8 +144,6 @@ class QuickState extends State<Quick> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int length() {
|
int length() {
|
||||||
// print(_key.currentState.items);
|
|
||||||
// return 0;
|
|
||||||
return _key.currentState.items.where((item) => item is QuickBook).length;
|
return _key.currentState.items.where((item) => item is QuickBook).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,6 +163,8 @@ class QuickState extends State<Quick> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final width = widget.width / 4 - 10;
|
||||||
|
final height = (width / 0.7).roundToDouble();
|
||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
@ -176,39 +176,36 @@ class QuickState extends State<Quick> {
|
|||||||
style: TextStyle(color: Colors.grey, fontSize: 12),
|
style: TextStyle(color: Colors.grey, fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
DraggableContainer(
|
||||||
width: widget.width,
|
key: _key,
|
||||||
child: DraggableContainer(
|
slotMargin: EdgeInsets.only(bottom: 8, left: 6, right: 6),
|
||||||
key: _key,
|
slotSize: Size(width, height),
|
||||||
slotMargin: EdgeInsets.only(bottom: 8, left: 7, right: 7),
|
slotDecoration: BoxDecoration(color: Colors.grey.withOpacity(0.3)),
|
||||||
slotSize: Size(72, 100),
|
dragDecoration: BoxDecoration(
|
||||||
slotDecoration:
|
boxShadow: [BoxShadow(color: Colors.black, blurRadius: 10)]),
|
||||||
BoxDecoration(color: Colors.grey.withOpacity(0.3)),
|
items: _draggableItems,
|
||||||
dragDecoration: BoxDecoration(
|
onDraggableModeChanged: widget.draggableModeChanged,
|
||||||
boxShadow: [BoxShadow(color: Colors.black, blurRadius: 10)]),
|
onChanged: (List<DraggableItem> items) {
|
||||||
items: _draggableItems,
|
id.clear();
|
||||||
onDraggableModeChanged: widget.draggableModeChanged,
|
items.forEach((item) {
|
||||||
onChanged: (List<DraggableItem> items) {
|
if (item is QuickBook) id.add(item.book.aid);
|
||||||
id.clear();
|
});
|
||||||
items.forEach((item) {
|
Data.addQuickAll(id);
|
||||||
if (item is QuickBook) id.add(item.book.aid);
|
final nullIndex = items.indexOf(null);
|
||||||
});
|
final buttonIndex = items.indexOf(_addButton);
|
||||||
Data.addQuickAll(id);
|
print('null $nullIndex, button $buttonIndex');
|
||||||
final nullIndex = items.indexOf(null);
|
if (nullIndex > -1 && buttonIndex == -1) {
|
||||||
final buttonIndex = items.indexOf(_addButton);
|
_key.currentState
|
||||||
print('null $nullIndex, button $buttonIndex');
|
.insteadOfIndex(nullIndex, _addButton, triggerEvent: false);
|
||||||
if (nullIndex > -1 && buttonIndex == -1) {
|
} else if (nullIndex > -1 &&
|
||||||
_key.currentState.insteadOfIndex(nullIndex, _addButton,
|
buttonIndex > -1 &&
|
||||||
triggerEvent: false);
|
nullIndex < buttonIndex) {
|
||||||
} else if (nullIndex > -1 &&
|
_key.currentState.removeItem(_addButton);
|
||||||
buttonIndex > -1 &&
|
_key.currentState
|
||||||
nullIndex < buttonIndex) {
|
.insteadOfIndex(nullIndex, _addButton, triggerEvent: false);
|
||||||
_key.currentState.removeItem(_addButton);
|
}
|
||||||
_key.currentState.insteadOfIndex(nullIndex, _addButton,
|
},
|
||||||
triggerEvent: false);
|
),
|
||||||
}
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user