完成页面信息的样式
parent
1aab54a6d8
commit
23264dfd7e
@ -0,0 +1,111 @@
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class AutoWrap extends StatefulWidget {
|
||||
final List<Widget> children;
|
||||
|
||||
const AutoWrap({super.key, required this.children});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _AutoWrapState();
|
||||
}
|
||||
|
||||
class _AutoWrapState extends State<AutoWrap> {
|
||||
var _computed = false;
|
||||
var _isFirstBuild = true;
|
||||
final Map<int, double> _childWidth = {};
|
||||
var _isRow = true;
|
||||
|
||||
void handleSizeChange(Size size, int id) {
|
||||
_childWidth[id] = size.width;
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant AutoWrap oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (_isFirstBuild) {
|
||||
_isFirstBuild = false;
|
||||
var sum = _childWidth.values.reduce((value, element) => value + element);
|
||||
var width = context.size?.width ?? 0;
|
||||
|
||||
setState(() {
|
||||
_computed = true;
|
||||
_isRow = sum < width;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!_computed) {
|
||||
List<Widget> stackChildren = [];
|
||||
for (var i = 0; i < widget.children.length; i++) {
|
||||
// Only first child is visible
|
||||
stackChildren.add(
|
||||
Opacity(
|
||||
opacity: i == 0 ? 1 : 0,
|
||||
child: WidgetSizeOffsetWrapper(
|
||||
id: i,
|
||||
onSizeChange: handleSizeChange,
|
||||
child: widget.children[i],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Stack(
|
||||
children: stackChildren,
|
||||
);
|
||||
} else {
|
||||
if (_isRow) {
|
||||
return Row(children: widget.children);
|
||||
} else {
|
||||
return Column(children: widget.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef OnWidgetSizeChange = void Function(Size size, int id);
|
||||
|
||||
class WidgetSizeRenderObject extends RenderProxyBox {
|
||||
final OnWidgetSizeChange onSizeChange;
|
||||
final int id;
|
||||
Size? currentSize;
|
||||
|
||||
WidgetSizeRenderObject(this.onSizeChange, this.id);
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
super.performLayout();
|
||||
|
||||
try {
|
||||
Size? newSize = child?.size;
|
||||
|
||||
if (newSize != null && currentSize != newSize) {
|
||||
currentSize = newSize;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
onSizeChange(newSize, id);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WidgetSizeOffsetWrapper extends SingleChildRenderObjectWidget {
|
||||
final OnWidgetSizeChange onSizeChange;
|
||||
final int id;
|
||||
|
||||
const WidgetSizeOffsetWrapper({
|
||||
Key? key,
|
||||
required this.onSizeChange,
|
||||
required this.id,
|
||||
required Widget child,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
return WidgetSizeRenderObject(onSizeChange, id);
|
||||
}
|
||||
}
|
@ -0,0 +1,213 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:isekai_wiki/api/mw/list.dart';
|
||||
import 'package:isekai_wiki/api/mw/mw_api.dart';
|
||||
import 'package:isekai_wiki/api/response/page_info.dart';
|
||||
import 'package:isekai_wiki/components/auto_wrap.dart';
|
||||
import 'package:isekai_wiki/components/isekai_nav_bar.dart';
|
||||
import 'package:isekai_wiki/components/isekai_page_scaffold.dart';
|
||||
import 'package:isekai_wiki/global.dart';
|
||||
import 'package:isekai_wiki/reactive/reactive.dart';
|
||||
import 'package:isekai_wiki/utils/dialog.dart';
|
||||
import 'package:isekai_wiki/utils/error.dart';
|
||||
|
||||
class WikiInfoPageController extends GetxController {
|
||||
var pageTitle = "".obs;
|
||||
var pageId = 0.obs;
|
||||
var pageInfo = Rx<PageInfo?>(null);
|
||||
var loadingPageInfo = true.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 150)).then((_) {
|
||||
loadPageInfo();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> loadPageInfo() async {
|
||||
if (pageTitle.isNotEmpty || pageId.value != 0) {
|
||||
try {
|
||||
// 加载页面信息
|
||||
loadingPageInfo.value = true;
|
||||
MWResponse<List<PageInfo>> pageInfoRes;
|
||||
if (pageId.value != 0) {
|
||||
pageInfoRes = await MWApiList.getPageInfoList(pageids: [
|
||||
pageId.value
|
||||
], prop: [
|
||||
"extracts",
|
||||
"info",
|
||||
"pageimages",
|
||||
"pageviews"
|
||||
], extraParams: {
|
||||
"inprop":
|
||||
"url|protection|watched|watchers|visitingwatchers|displaytitle",
|
||||
});
|
||||
} else {
|
||||
pageInfoRes =
|
||||
await MWApiList.getPageInfoList(titles: [pageTitle.value]);
|
||||
}
|
||||
if (pageInfoRes.data.isEmpty) {
|
||||
throw MWApiErrorException(code: 'no-page', info: "页面信息丢失");
|
||||
}
|
||||
|
||||
pageInfo.value = pageInfoRes.data[0];
|
||||
pageId.value = pageInfo.value!.pageid;
|
||||
pageTitle.value = pageInfo.value!.title;
|
||||
} catch (err, stack) {
|
||||
alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err),
|
||||
title: "错误");
|
||||
if (kDebugMode) {
|
||||
print("Exception in page: $err");
|
||||
stack.printError();
|
||||
}
|
||||
} finally {
|
||||
loadingPageInfo.value = false;
|
||||
}
|
||||
} else {
|
||||
alert(Get.overlayContext!, "页面不存在");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WikiInfoPage extends StatefulWidget {
|
||||
final String? targetPage;
|
||||
final int? targetPageId;
|
||||
|
||||
const WikiInfoPage({super.key, this.targetPage, this.targetPageId});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _WikiInfoPageState();
|
||||
}
|
||||
|
||||
class _WikiInfoPageState extends ReactiveState<WikiInfoPage> {
|
||||
WikiInfoPageController c = WikiInfoPageController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
c = Get.put(c);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
c.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void receiveProps() {
|
||||
c.pageId.value = widget.targetPageId ?? 0;
|
||||
c.pageTitle.value = widget.targetPage ?? "";
|
||||
}
|
||||
|
||||
Widget buildLoading(BuildContext context) {
|
||||
return CupertinoListSection.insetGrouped(
|
||||
header: Text("基本信息"),
|
||||
backgroundColor: CupertinoTheme.of(context).scaffoldBackgroundColor,
|
||||
children: <CupertinoListTile>[
|
||||
CupertinoListTile.notched(
|
||||
title: Center(
|
||||
child: CupertinoActivityIndicator(
|
||||
radius: 10 * MediaQuery.of(context).textScaleFactor),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
CupertinoListTile buildPageInfo(
|
||||
BuildContext context, String label, dynamic value,
|
||||
{VoidFutureCallback? onTap}) {
|
||||
final Locale appLocale = Localizations.localeOf(context);
|
||||
var valueStr = "未知";
|
||||
if (value is String) {
|
||||
valueStr = value;
|
||||
}
|
||||
if (value is int) {
|
||||
valueStr =
|
||||
NumberFormat.decimalPattern(appLocale.toLanguageTag()).format(value);
|
||||
} else if (value is double) {
|
||||
var pattern = NumberFormat.decimalPattern(appLocale.toLanguageTag());
|
||||
pattern.maximumFractionDigits = 2;
|
||||
valueStr = pattern.format(value);
|
||||
} else if (value is DateTime) {
|
||||
// ignore: prefer_interpolation_to_compose_strings
|
||||
valueStr = DateFormat.yMMMd(appLocale.toLanguageTag()).format(value) +
|
||||
" " +
|
||||
DateFormat.Hms(appLocale.toLanguageTag()).format(value);
|
||||
}
|
||||
|
||||
return CupertinoListTile.notched(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(20.0, 10.0, 14.0, 10.0),
|
||||
title: Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: AutoWrap(
|
||||
children: [
|
||||
Text(label),
|
||||
Text(
|
||||
valueStr,
|
||||
style: TextStyle(
|
||||
color: CupertinoColors.systemGrey2.resolveFrom(context)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
trailing: onTap != null ? const CupertinoListTileChevron() : null,
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildPageInfoList(BuildContext context) {
|
||||
var pageInfo = c.pageInfo.value;
|
||||
return Column(
|
||||
children: [
|
||||
Obx(
|
||||
() => CupertinoListSection.insetGrouped(
|
||||
additionalDividerMargin: 6,
|
||||
header: const Text("基本信息"),
|
||||
backgroundColor: CupertinoTheme.of(context).scaffoldBackgroundColor,
|
||||
children: <CupertinoListTile>[
|
||||
buildPageInfo(
|
||||
context,
|
||||
"显示标题",
|
||||
pageInfo?.mainTitle,
|
||||
onTap: () async {},
|
||||
),
|
||||
buildPageInfo(context, "页面长度(字节)", pageInfo?.length),
|
||||
buildPageInfo(context, "页面ID", pageInfo?.pageid),
|
||||
buildPageInfo(context, "页面内容语言", pageInfo?.pagelanguage),
|
||||
buildPageInfo(context, "页面内容类型", pageInfo?.contentmodel),
|
||||
buildPageInfo(context, "收藏者数", pageInfo?.watchers),
|
||||
buildPageInfo(context, "最后修改于", pageInfo?.updatedTime),
|
||||
buildPageInfo(context, "该页面的子页面数", null),
|
||||
buildPageInfo(context, "过去30天的页面访问量", null),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget render(BuildContext context) {
|
||||
return IsekaiPageScaffold(
|
||||
navigationBar: const IsekaiNavigationBar(
|
||||
middle: Text("页面信息"),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: ListView(
|
||||
children: [
|
||||
Obx(() => c.loadingPageInfo.value
|
||||
? buildLoading(context)
|
||||
: buildPageInfoList(context))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue