preload image from network in gallery mode
This commit is contained in:
parent
fd175feb80
commit
f5adcbd2e0
15
README.md
15
README.md
|
@ -109,16 +109,17 @@ VPN->代理->分流, 这三个功能如果同时设置, 您会在您手机的VPN
|
||||||
sudo apt install xorg-dev
|
sudo apt install xorg-dev
|
||||||
```
|
```
|
||||||
- 字体不显示
|
- 字体不显示
|
||||||
1. 将字体文件复制到项目目录下
|
|
||||||
```shell
|
```shell
|
||||||
cp /usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf fonts/
|
# 将字体文件复制到项目目录下
|
||||||
|
mkdir -p fonts
|
||||||
|
cp -f /usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf fonts/
|
||||||
```
|
```
|
||||||
2. 设置flutter打包的字体
|
|
||||||
```yaml
|
```yaml
|
||||||
fonts:
|
# 编辑 pubspec.yaml
|
||||||
- family: Roboto
|
fonts:
|
||||||
fonts:
|
- family: Roboto
|
||||||
- asset: fonts/DroidSansFallbackFull.ttf
|
fonts:
|
||||||
|
- asset: fonts/DroidSansFallbackFull.ttf
|
||||||
```
|
```
|
||||||
|
|
||||||
### 移动端 (gomobile)
|
### 移动端 (gomobile)
|
||||||
|
|
|
@ -35,10 +35,11 @@ func main() {
|
||||||
if height <= 0 {
|
if height <= 0 {
|
||||||
height = 900
|
height = 900
|
||||||
}
|
}
|
||||||
sizeOption := flutter.WindowInitialDimensions(width, height)
|
var runOptions []flutter.Option
|
||||||
options = append(options, sizeOption)
|
runOptions = append(runOptions, flutter.WindowInitialDimensions(width, height))
|
||||||
//
|
runOptions = append(runOptions, options...)
|
||||||
err := flutter.Run(append(options, mainOptions...)...)
|
// ------
|
||||||
|
err := flutter.Run(append(runOptions, mainOptions...)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -169,37 +169,11 @@ func remoteImageData(params string) (string, error) {
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
cache := comic_center.FindRemoteImage(fileServer, path)
|
cache := comic_center.FindRemoteImage(fileServer, path)
|
||||||
if cache == nil {
|
if cache == nil {
|
||||||
buff, img, format, err := decodeFromUrl(fileServer, path)
|
remote, err := decodeAndSaveImage(fileServer, path)
|
||||||
if err != nil {
|
|
||||||
println(fmt.Sprintf("decode error : %s/static/%s %s", fileServer, path, err.Error()))
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
local :=
|
|
||||||
fmt.Sprintf("%x",
|
|
||||||
md5.Sum([]byte(fmt.Sprintf("%s$%s", fileServer, path))),
|
|
||||||
)
|
|
||||||
real := remotePath(local)
|
|
||||||
err = ioutil.WriteFile(
|
|
||||||
real,
|
|
||||||
buff, os.FileMode(0600),
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
remote := comic_center.RemoteImage{
|
cache = remote
|
||||||
FileServer: fileServer,
|
|
||||||
Path: path,
|
|
||||||
FileSize: int64(len(buff)),
|
|
||||||
Format: format,
|
|
||||||
Width: int32(img.Bounds().Dx()),
|
|
||||||
Height: int32(img.Bounds().Dy()),
|
|
||||||
LocalPath: local,
|
|
||||||
}
|
|
||||||
err = comic_center.SaveRemoteImage(&remote)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
cache = &remote
|
|
||||||
}
|
}
|
||||||
display := DisplayImageData{
|
display := DisplayImageData{
|
||||||
FileSize: cache.FileSize,
|
FileSize: cache.FileSize,
|
||||||
|
@ -211,6 +185,56 @@ func remoteImageData(params string) (string, error) {
|
||||||
return serialize(&display, nil)
|
return serialize(&display, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func remoteImagePreload(params string) error {
|
||||||
|
var paramsStruct struct {
|
||||||
|
FileServer string `json:"fileServer"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
json.Unmarshal([]byte(params), ¶msStruct)
|
||||||
|
fileServer := paramsStruct.FileServer
|
||||||
|
path := paramsStruct.Path
|
||||||
|
lock := utils.HashLock(fmt.Sprintf("%s$%s", fileServer, path))
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
cache := comic_center.FindRemoteImage(fileServer, path)
|
||||||
|
var err error
|
||||||
|
if cache == nil {
|
||||||
|
_, err = decodeAndSaveImage(fileServer, path)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeAndSaveImage(fileServer string, path string) (*comic_center.RemoteImage, error) {
|
||||||
|
buff, img, format, err := decodeFromUrl(fileServer, path)
|
||||||
|
if err != nil {
|
||||||
|
println(fmt.Sprintf("decode error : %s/static/%s %s", fileServer, path, err.Error()))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
local :=
|
||||||
|
fmt.Sprintf("%x",
|
||||||
|
md5.Sum([]byte(fmt.Sprintf("%s$%s", fileServer, path))),
|
||||||
|
)
|
||||||
|
real := remotePath(local)
|
||||||
|
err = ioutil.WriteFile(
|
||||||
|
real,
|
||||||
|
buff, os.FileMode(0600),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
remote := comic_center.RemoteImage{
|
||||||
|
FileServer: fileServer,
|
||||||
|
Path: path,
|
||||||
|
FileSize: int64(len(buff)),
|
||||||
|
Format: format,
|
||||||
|
Width: int32(img.Bounds().Dx()),
|
||||||
|
Height: int32(img.Bounds().Dy()),
|
||||||
|
LocalPath: local,
|
||||||
|
}
|
||||||
|
err = comic_center.SaveRemoteImage(&remote)
|
||||||
|
return &remote, err
|
||||||
|
}
|
||||||
|
|
||||||
func downloadImagePath(path string) (string, error) {
|
func downloadImagePath(path string) (string, error) {
|
||||||
return downloadPath(path), nil
|
return downloadPath(path), nil
|
||||||
}
|
}
|
||||||
|
@ -565,6 +589,8 @@ func FlatInvoke(method string, params string) (string, error) {
|
||||||
return "", importComicDownloadUsingSocket(params)
|
return "", importComicDownloadUsingSocket(params)
|
||||||
case "remoteImageData":
|
case "remoteImageData":
|
||||||
return remoteImageData(params)
|
return remoteImageData(params)
|
||||||
|
case "remoteImagePreload":
|
||||||
|
return "", remoteImagePreload(params)
|
||||||
case "clientIpSet":
|
case "clientIpSet":
|
||||||
return clientIpSet()
|
return clientIpSet()
|
||||||
case "downloadImagePath":
|
case "downloadImagePath":
|
||||||
|
|
|
@ -150,6 +150,13 @@ class Method {
|
||||||
return RemoteImageData.fromJson(json.decode(data));
|
return RemoteImageData.fromJson(json.decode(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<dynamic> remoteImagePreload(String fileServer, String path) async {
|
||||||
|
return _flatInvoke("remoteImagePreload", {
|
||||||
|
"fileServer": fileServer,
|
||||||
|
"path": path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<String> downloadImagePath(String path) async {
|
Future<String> downloadImagePath(String path) async {
|
||||||
return await _flatInvoke("downloadImagePath", path);
|
return await _flatInvoke("downloadImagePath", path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pikapi/basic/Method.dart';
|
||||||
|
|
||||||
|
import '../Common.dart';
|
||||||
|
|
||||||
|
const galleryPrePreloadCount = 1;
|
||||||
|
const galleryPreloadCount = 2;
|
|
@ -50,6 +50,6 @@ Widget shadowCategoriesActionButton(BuildContext context) {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
chooseShadowCategories(context);
|
chooseShadowCategories(context);
|
||||||
},
|
},
|
||||||
icon: Icon(Icons.dnd_forwardslash),
|
icon: Icon(Icons.hide_source),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,7 +165,8 @@ Future<dynamic> initTheme() async {
|
||||||
_androidVersion = await method.androidGetVersion();
|
_androidVersion = await method.androidGetVersion();
|
||||||
if (_androidVersion >= 29) {
|
if (_androidVersion >= 29) {
|
||||||
_androidNightMode =
|
_androidNightMode =
|
||||||
(await method.loadProperty(_nightModePropertyName, "false")) == "true";
|
(await method.loadProperty(_nightModePropertyName, "false")) ==
|
||||||
|
"true";
|
||||||
_systemNight = (await method.androidGetUiMode()) == "NIGHT";
|
_systemNight = (await method.androidGetUiMode()) == "NIGHT";
|
||||||
EventChannel("ui_mode").receiveBroadcastStream().listen((event) {
|
EventChannel("ui_mode").receiveBroadcastStream().listen((event) {
|
||||||
_systemNight = "$event" == "NIGHT";
|
_systemNight = "$event" == "NIGHT";
|
||||||
|
@ -185,23 +186,49 @@ Future<dynamic> chooseTheme(BuildContext buildContext) async {
|
||||||
builder: (BuildContext context, StateSetter setState) {
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
var list = <SimpleDialogOption>[];
|
var list = <SimpleDialogOption>[];
|
||||||
if (_androidVersion >= 29) {
|
if (_androidVersion >= 29) {
|
||||||
|
var onChange = (bool? v) async {
|
||||||
|
if (v != null) {
|
||||||
|
await method.saveProperty(
|
||||||
|
_nightModePropertyName, "$v");
|
||||||
|
_androidNightMode = v;
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
themeEvent.broadcast();
|
||||||
|
};
|
||||||
list.add(
|
list.add(
|
||||||
SimpleDialogOption(
|
SimpleDialogOption(
|
||||||
child: Row(
|
child: GestureDetector(
|
||||||
children: [
|
onTap: () {
|
||||||
Checkbox(
|
onChange(!_androidNightMode);
|
||||||
value: _androidNightMode,
|
},
|
||||||
onChanged: (bool? v) async {
|
child: Container(
|
||||||
if (v != null) {
|
margin: EdgeInsets.only(top: 3, bottom: 3),
|
||||||
await method.saveProperty(
|
decoration: BoxDecoration(
|
||||||
_nightModePropertyName, "$v");
|
border: Border(
|
||||||
_androidNightMode = v;
|
top: BorderSide(
|
||||||
}
|
color: Theme
|
||||||
setState(() {});
|
.of(context)
|
||||||
themeEvent.broadcast();
|
.dividerColor,
|
||||||
}),
|
width: 0.5,
|
||||||
Text("随手机进入夜间模式"),
|
),
|
||||||
],
|
bottom: BorderSide(
|
||||||
|
color: Theme
|
||||||
|
.of(context)
|
||||||
|
.dividerColor,
|
||||||
|
width: 0.5
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: _androidNightMode,
|
||||||
|
onChanged: onChange,
|
||||||
|
),
|
||||||
|
Text("随手机进入夜间模式"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const ERROR_TYPE_NETWORK = "NETWORK_ERROR";
|
const ERROR_TYPE_NETWORK = "NETWORK_ERROR";
|
||||||
|
const ERROR_TYPE_PERMISSION = "PERMISSION_ERROR";
|
||||||
|
|
||||||
// 错误的类型, 方便照展示和谐的提示
|
// 错误的类型, 方便照展示和谐的提示
|
||||||
String errorType(String error) {
|
String errorType(String error) {
|
||||||
|
@ -11,5 +12,8 @@ String errorType(String error) {
|
||||||
error.contains("deadline")) {
|
error.contains("deadline")) {
|
||||||
return ERROR_TYPE_NETWORK;
|
return ERROR_TYPE_NETWORK;
|
||||||
}
|
}
|
||||||
|
if (error.contains("permission denied")) {
|
||||||
|
return ERROR_TYPE_PERMISSION;
|
||||||
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,23 +60,6 @@ class _ComicsScreenState extends State<ComicsScreen> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
shadowCategoriesEvent.subscribe(_onShadowChange);
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
shadowCategoriesEvent.unsubscribe(_onShadowChange);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onShadowChange(EventArgs? args) {
|
|
||||||
setState(() {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _chooseCategoryAction() => IconButton(
|
Widget _chooseCategoryAction() => IconButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
String? category = await chooseListDialog(context, '请选择分类', [
|
String? category = await chooseListDialog(context, '请选择分类', [
|
||||||
|
@ -142,6 +125,7 @@ class _ComicsScreenState extends State<ComicsScreen> {
|
||||||
appBar = AppBar(
|
appBar = AppBar(
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
actions: [
|
actions: [
|
||||||
|
shadowCategoriesActionButton(context),
|
||||||
chooseLayoutAction(context),
|
chooseLayoutAction(context),
|
||||||
_chooseCategoryAction(),
|
_chooseCategoryAction(),
|
||||||
],
|
],
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:pikapi/basic/Entities.dart';
|
import 'package:pikapi/basic/Entities.dart';
|
||||||
import 'package:pikapi/basic/Method.dart';
|
import 'package:pikapi/basic/Method.dart';
|
||||||
import 'package:pikapi/basic/config/ListLayout.dart';
|
import 'package:pikapi/basic/config/ListLayout.dart';
|
||||||
|
import 'package:pikapi/basic/config/ShadowCategories.dart';
|
||||||
|
|
||||||
import 'components/ComicListBuilder.dart';
|
import 'components/ComicListBuilder.dart';
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ class _RandomComicsScreenState extends State<RandomComicsScreen> {
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('随机本子'),
|
title: Text('随机本子'),
|
||||||
actions: [
|
actions: [
|
||||||
|
shadowCategoriesActionButton(context),
|
||||||
chooseLayoutAction(context),
|
chooseLayoutAction(context),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,10 +2,12 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:pikapi/basic/Entities.dart';
|
import 'package:pikapi/basic/Entities.dart';
|
||||||
import 'package:pikapi/basic/Method.dart';
|
import 'package:pikapi/basic/Method.dart';
|
||||||
import 'package:pikapi/basic/config/ListLayout.dart';
|
import 'package:pikapi/basic/config/ListLayout.dart';
|
||||||
|
import 'package:pikapi/basic/config/ShadowCategories.dart';
|
||||||
|
|
||||||
import 'components/ComicListBuilder.dart';
|
import 'components/ComicListBuilder.dart';
|
||||||
|
|
||||||
class RankingsScreen extends StatelessWidget {
|
class RankingsScreen extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
|
@ -13,6 +15,7 @@ class RankingsScreen extends StatelessWidget {
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('排行榜'),
|
title: Text('排行榜'),
|
||||||
actions: [
|
actions: [
|
||||||
|
shadowCategoriesActionButton(context),
|
||||||
chooseLayoutAction(context),
|
chooseLayoutAction(context),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -96,23 +96,6 @@ class _SearchScreenState extends State<SearchScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
shadowCategoriesEvent.subscribe(_onShadowChange);
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
shadowCategoriesEvent.unsubscribe(_onShadowChange);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onShadowChange(EventArgs? args) {
|
|
||||||
setState(() {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
|
@ -1,28 +1,51 @@
|
||||||
|
import 'package:event/event.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pikapi/basic/Entities.dart';
|
import 'package:pikapi/basic/Entities.dart';
|
||||||
|
import 'package:pikapi/basic/config/ShadowCategories.dart';
|
||||||
import 'package:pikapi/screens/components/ComicList.dart';
|
import 'package:pikapi/screens/components/ComicList.dart';
|
||||||
import 'package:pikapi/screens/components/FitButton.dart';
|
import 'package:pikapi/screens/components/FitButton.dart';
|
||||||
import 'ContentBuilder.dart';
|
import 'ContentBuilder.dart';
|
||||||
|
|
||||||
class ComicListBuilder extends StatelessWidget {
|
class ComicListBuilder extends StatefulWidget {
|
||||||
final Future<List<ComicSimple>> future;
|
final Future<List<ComicSimple>> future;
|
||||||
final Future Function() reload;
|
final Future Function() reload;
|
||||||
|
|
||||||
ComicListBuilder(this.future, this.reload);
|
ComicListBuilder(this.future, this.reload);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _ComicListBuilderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ComicListBuilderState extends State<ComicListBuilder> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
shadowCategoriesEvent.subscribe(_onShadowChange);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
shadowCategoriesEvent.unsubscribe(_onShadowChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onShadowChange(EventArgs? args) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ContentBuilder(
|
return ContentBuilder(
|
||||||
future: future,
|
future: widget.future,
|
||||||
onRefresh: reload,
|
onRefresh: widget.reload,
|
||||||
successBuilder:
|
successBuilder:
|
||||||
(BuildContext context, AsyncSnapshot<List<ComicSimple>> snapshot) {
|
(BuildContext context, AsyncSnapshot<List<ComicSimple>> snapshot) {
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: reload,
|
onRefresh: widget.reload,
|
||||||
child: ComicList(
|
child: ComicList(
|
||||||
snapshot.data!,
|
snapshot.data!,
|
||||||
appendWidget: FitButton(
|
appendWidget: FitButton(
|
||||||
onPressed: reload,
|
onPressed: widget.reload,
|
||||||
text: '刷新',
|
text: '刷新',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import 'package:event/event.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:pikapi/basic/Entities.dart';
|
import 'package:pikapi/basic/Entities.dart';
|
||||||
import 'package:pikapi/basic/config/PagerAction.dart';
|
import 'package:pikapi/basic/config/PagerAction.dart';
|
||||||
|
import 'package:pikapi/basic/config/ShadowCategories.dart';
|
||||||
import 'package:pikapi/basic/enum/Sort.dart';
|
import 'package:pikapi/basic/enum/Sort.dart';
|
||||||
import 'package:pikapi/screens/components/ComicList.dart';
|
import 'package:pikapi/screens/components/ComicList.dart';
|
||||||
import 'package:pikapi/screens/components/ContentError.dart';
|
import 'package:pikapi/screens/components/ContentError.dart';
|
||||||
|
@ -9,18 +11,41 @@ import 'package:pikapi/screens/components/FitButton.dart';
|
||||||
import 'ContentLoading.dart';
|
import 'ContentLoading.dart';
|
||||||
|
|
||||||
// 漫画列页
|
// 漫画列页
|
||||||
class ComicPager extends StatelessWidget {
|
class ComicPager extends StatefulWidget {
|
||||||
final Future<ComicsPage> Function(String sort, int page) fetchPage;
|
final Future<ComicsPage> Function(String sort, int page) fetchPage;
|
||||||
|
|
||||||
const ComicPager({required this.fetchPage});
|
const ComicPager({required this.fetchPage});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _ComicPagerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ComicPagerState extends State<ComicPager> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
shadowCategoriesEvent.subscribe(_onShadowChange);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
shadowCategoriesEvent.unsubscribe(_onShadowChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onShadowChange(EventArgs? args) {
|
||||||
|
setState(() {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
switch (currentPagerAction) {
|
switch (currentPagerAction) {
|
||||||
case PagerAction.CONTROLLER:
|
case PagerAction.CONTROLLER:
|
||||||
return ControllerComicPager(fetchPage: fetchPage);
|
return ControllerComicPager(fetchPage: widget.fetchPage);
|
||||||
case PagerAction.STREAM:
|
case PagerAction.STREAM:
|
||||||
return StreamComicPager(fetchPage: fetchPage);
|
return StreamComicPager(fetchPage: widget.fetchPage);
|
||||||
default:
|
default:
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,24 +24,67 @@ class ContentError extends StatelessWidget {
|
||||||
case ERROR_TYPE_NETWORK:
|
case ERROR_TYPE_NETWORK:
|
||||||
message = "连接不上啦, 请检查网络";
|
message = "连接不上啦, 请检查网络";
|
||||||
break;
|
break;
|
||||||
|
case ERROR_TYPE_PERMISSION:
|
||||||
|
message = "没有权限或路径不可用";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
message = "啊哦, 被玩坏了";
|
message = "啊哦, 被玩坏了";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
|
return LayoutBuilder(
|
||||||
print("$error");
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
print("$stackTrace");
|
print("$error");
|
||||||
var width = constraints.maxWidth;
|
print("$stackTrace");
|
||||||
var height = constraints.maxHeight;
|
var width = constraints.maxWidth;
|
||||||
var min = width < height ? width : height;
|
var height = constraints.maxHeight;
|
||||||
var iconSize = min / 2.3;
|
var min = width < height ? width : height;
|
||||||
var textSize = min / 16;
|
var iconSize = min / 2.3;
|
||||||
var tipSize = min / 20;
|
var textSize = min / 16;
|
||||||
var infoSize = min / 30;
|
var tipSize = min / 20;
|
||||||
if (contentFailedReloadAction ==
|
var infoSize = min / 30;
|
||||||
ContentFailedReloadAction.TOUCH_LOADER) {
|
if (contentFailedReloadAction ==
|
||||||
return GestureDetector(
|
ContentFailedReloadAction.TOUCH_LOADER) {
|
||||||
onTap: onRefresh,
|
return GestureDetector(
|
||||||
|
onTap: onRefresh,
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: height,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(child: Container()),
|
||||||
|
Container(
|
||||||
|
child: Icon(
|
||||||
|
Icons.wifi_off_rounded,
|
||||||
|
size: iconSize,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(height: min / 10),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: 30,
|
||||||
|
right: 30,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
message,
|
||||||
|
style: TextStyle(fontSize: textSize),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text('(点击刷新)', style: TextStyle(fontSize: tipSize)),
|
||||||
|
Container(height: min / 15),
|
||||||
|
Text('$error', style: TextStyle(fontSize: infoSize)),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: onRefresh,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
|
@ -68,7 +111,7 @@ class ContentError extends StatelessWidget {
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text('(点击刷新)', style: TextStyle(fontSize: tipSize)),
|
Text('(下拉刷新)', style: TextStyle(fontSize: tipSize)),
|
||||||
Container(height: min / 15),
|
Container(height: min / 15),
|
||||||
Text('$error', style: TextStyle(fontSize: infoSize)),
|
Text('$error', style: TextStyle(fontSize: infoSize)),
|
||||||
Expanded(child: Container()),
|
Expanded(child: Container()),
|
||||||
|
@ -78,45 +121,7 @@ class ContentError extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
return RefreshIndicator(
|
);
|
||||||
onRefresh: onRefresh,
|
|
||||||
child: ListView(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
height: height,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Expanded(child: Container()),
|
|
||||||
Container(
|
|
||||||
child: Icon(
|
|
||||||
Icons.wifi_off_rounded,
|
|
||||||
size: iconSize,
|
|
||||||
color: Colors.grey.shade600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(height: min / 10),
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
left: 30,
|
|
||||||
right: 30,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
message,
|
|
||||||
style: TextStyle(fontSize: textSize),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text('(下拉刷新)', style: TextStyle(fontSize: tipSize)),
|
|
||||||
Container(height: min / 15),
|
|
||||||
Text('$error', style: TextStyle(fontSize: infoSize)),
|
|
||||||
Expanded(child: Container()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,10 @@ import 'package:pikapi/basic/Cross.dart';
|
||||||
import 'package:pikapi/basic/Entities.dart';
|
import 'package:pikapi/basic/Entities.dart';
|
||||||
import 'package:pikapi/basic/Method.dart';
|
import 'package:pikapi/basic/Method.dart';
|
||||||
import 'package:pikapi/basic/config/FullScreenAction.dart';
|
import 'package:pikapi/basic/config/FullScreenAction.dart';
|
||||||
|
import 'package:pikapi/basic/config/GalleryPreloadCount.dart';
|
||||||
import 'package:pikapi/basic/config/KeyboardController.dart';
|
import 'package:pikapi/basic/config/KeyboardController.dart';
|
||||||
import 'package:pikapi/basic/config/ReaderDirection.dart';
|
import 'package:pikapi/basic/config/ReaderDirection.dart';
|
||||||
import 'package:pikapi/basic/config/ReaderType.dart';
|
import 'package:pikapi/basic/config/ReaderType.dart';
|
||||||
import 'package:pikapi/basic/config/VolumeController.dart';
|
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
import '../FilePhotoViewScreen.dart';
|
import '../FilePhotoViewScreen.dart';
|
||||||
import 'gesture_zoom_box.dart';
|
import 'gesture_zoom_box.dart';
|
||||||
|
@ -685,6 +685,27 @@ class _GalleryReaderState extends State<_GalleryReader> {
|
||||||
_current = value + 1;
|
_current = value + 1;
|
||||||
_slider = value + 1;
|
_slider = value + 1;
|
||||||
widget.struct.onPositionChange(value);
|
widget.struct.onPositionChange(value);
|
||||||
|
if (galleryPrePreloadCount > 0) {
|
||||||
|
for (var count = 1;
|
||||||
|
count <= galleryPrePreloadCount && value - count >= 0;
|
||||||
|
count++) {
|
||||||
|
var target = widget.struct.images[value - count];
|
||||||
|
if (target.downloadLocalPath == null) {
|
||||||
|
method.remoteImagePreload(target.fileServer, target.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (galleryPreloadCount > 0) {
|
||||||
|
for (var count = 1;
|
||||||
|
count <= galleryPreloadCount &&
|
||||||
|
value + count < widget.struct.images.length;
|
||||||
|
count++) {
|
||||||
|
var target = widget.struct.images[value + count];
|
||||||
|
if (target.downloadLocalPath == null) {
|
||||||
|
method.remoteImagePreload(target.fileServer, target.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
itemCount: widget.struct.images.length,
|
itemCount: widget.struct.images.length,
|
||||||
|
|
|
@ -39,9 +39,9 @@ dependencies:
|
||||||
filesystem_picker: ^2.0.0-nullsafety.0
|
filesystem_picker: ^2.0.0-nullsafety.0
|
||||||
url_launcher: ^6.0.9
|
url_launcher: ^6.0.9
|
||||||
clipboard: ^0.1.3
|
clipboard: ^0.1.3
|
||||||
flutter_datetime_picker: ^1.5.1
|
|
||||||
photo_view: ^0.12.0
|
photo_view: ^0.12.0
|
||||||
multi_select_flutter: ^4.0.0
|
multi_select_flutter: ^4.0.0
|
||||||
|
flutter_datetime_picker: ^1.5.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in New Issue