完成目录

main
落雨楓 2 years ago
parent 14e8f42b9f
commit 7ac20f877a

@ -1,7 +1,9 @@
var _InAppWebViewReady = false;
var enableScrollSpy = true;
var titleList = [];
var currentTitle = '_top';
var titleOffset = 10;
function onReadyStateChange() {
if (_InAppWebViewReady && document.readyState === "complete") {
@ -31,9 +33,9 @@ function debouce(callback, ms) {
}
document.addEventListener('scroll', debouce(function() {
if (!titleList || titleList.length === 0) return;
if (!enableScrollSpy || !titleList || titleList.length === 0) return true;
var offsetTop = (navigator.safeArea ? navigator.safeArea.top : 0) + 10;
var offsetTop = (navigator.safeArea ? navigator.safeArea.top : 0) + titleOffset;
var currentTitleEl = null;
var isFirstSection = false;
@ -69,19 +71,24 @@ document.addEventListener('scroll', debouce(function() {
}
}, 200), { passive: true });
var MugenApp = {
window.MugenApp = {
scrollToTitle: function (anchor) {
var el = document.getElementById(anchor);
if (el) {
var scrollTop = el.offsetTop;
var scrollTop = window.scrollY + el.getBoundingClientRect().top;
if (navigator.safeArea) {
scrollTop += navigator.safeArea.top;
scrollTop -= navigator.safeArea.top + titleOffset;
}
scrollTop = Math.max(0, scrollTop);
enableScrollSpy = false;
window.scrollTo({
top: scrollTop,
behavior: "smooth",
});
setTimeout(function() {
enableScrollSpy = true;
}, 50);
}
},
};

@ -1,3 +1,4 @@
import 'package:intl/intl.dart';
import 'package:isekai_wiki/api/mw/mw_api.dart';
import 'package:isekai_wiki/api/response/parse.dart';
import 'package:isekai_wiki/global.dart';
@ -48,6 +49,18 @@ class MWApiParse {
var mwRes = await MWApi.get("parse", params: query);
var parseInfo = MWParseInfo.fromJson(mwRes.data);
if (parseInfo.sections.isNotEmpty) {
parseInfo = parseInfo.copyWith(
sections: parseInfo.sections
.map(
(section) => section.copyWith(
line: Bidi.stripHtmlIfNeeded(section.line),
),
)
.toList(),
);
}
return mwRes.replaceData(parseInfo);
}
}

@ -3,7 +3,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'parse.freezed.dart';
part 'parse.g.dart';
@Freezed(copyWith: false)
@freezed
class MWParseCategoryInfo with _$MWParseCategoryInfo {
factory MWParseCategoryInfo({
required String category,
@ -14,7 +14,7 @@ class MWParseCategoryInfo with _$MWParseCategoryInfo {
_$MWParseCategoryInfoFromJson(json);
}
@Freezed(copyWith: false)
@freezed
class MWParseLangLinkInfo with _$MWParseLangLinkInfo {
factory MWParseLangLinkInfo({
required String lang,
@ -28,7 +28,7 @@ class MWParseLangLinkInfo with _$MWParseLangLinkInfo {
_$MWParseLangLinkInfoFromJson(json);
}
@Freezed(copyWith: false)
@freezed
class MWParsePageLinkInfo with _$MWParsePageLinkInfo {
factory MWParsePageLinkInfo({
required int ns,
@ -40,7 +40,7 @@ class MWParsePageLinkInfo with _$MWParsePageLinkInfo {
_$MWParsePageLinkInfoFromJson(json);
}
@Freezed(copyWith: false)
@freezed
class MWParseSectionInfo with _$MWParseSectionInfo {
factory MWParseSectionInfo({
required int toclevel,
@ -57,7 +57,7 @@ class MWParseSectionInfo with _$MWParseSectionInfo {
_$MWParseSectionInfoFromJson(json);
}
@Freezed(copyWith: false)
@freezed
class MWParseInfo with _$MWParseInfo {
factory MWParseInfo({
required String title,

@ -97,9 +97,7 @@ class IsekaiWikiApp extends StatelessWidget {
? Styles.materialLightTheme
: Styles.materialDarkTheme,
child: CupertinoTheme(
data:
Styles.cupertinoTheme.copyWith(brightness: brightness),
child: child),
data: Styles.cupertinoTheme.copyWith(brightness: brightness), child: child),
),
);
}

@ -25,8 +25,7 @@ class _OpacityGestureDetectorState extends State<OpacityGestureDetector> {
Widget build(BuildContext context) {
return Text(
widget.text,
style: const TextStyle(
color: Styles.themeNavTitleColor, fontWeight: FontWeight.normal),
style: const TextStyle(color: Styles.themeNavTitleColor, fontWeight: FontWeight.normal),
);
}
}
@ -34,8 +33,7 @@ class _OpacityGestureDetectorState extends State<OpacityGestureDetector> {
enum PointerActiveMode { none, hover, active }
class ClickableBuilder extends StatefulWidget {
final Widget Function(
BuildContext context, PointerActiveMode mode, Widget child) builder;
final Widget Function(BuildContext context, PointerActiveMode mode, Widget child) builder;
final Widget? child;
const ClickableBuilder({super.key, required this.builder, this.child});
@ -69,7 +67,7 @@ class _ClickableBuilder extends State<ClickableBuilder> {
setState(() {
isActive = true;
});
Future.delayed(const Duration(milliseconds: 300)).then((value) {
Future.delayed(const Duration(milliseconds: 150)).then((value) {
if (isPointerDown) {
isPersistActive = true;
} else {
@ -99,11 +97,14 @@ class _ClickableBuilder extends State<ClickableBuilder> {
innerItem = widget.builder(context, PointerActiveMode.active, child);
} else if (isHover) {
innerItem = widget.builder(context, PointerActiveMode.hover, child);
} else {
innerItem = widget.builder(context, PointerActiveMode.none, child);
}
innerItem = widget.builder(context, PointerActiveMode.none, child);
return Listener(
onPointerDown: (event) {},
onPointerDown: onPointerDown,
onPointerUp: onPointerUp,
onPointerCancel: onPointerUp,
child: MouseRegion(
onEnter: onMouseEnter,
onExit: onMouseExit,

@ -1,29 +1,178 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:isekai_wiki/api/response/parse.dart';
import 'package:isekai_wiki/components/gesture_detector.dart';
import 'package:isekai_wiki/reactive/reactive.dart';
import 'package:isekai_wiki/components/safearea_builder.dart';
import 'package:isekai_wiki/styles.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
class TOCList extends StatefulWidget {
const TOCList({super.key});
final String activedItem;
final bool hasFirstSection;
final List<MWParseSectionInfo> sections;
final void Function(String anchor)? onSwitchTitle;
const TOCList({
super.key,
required this.sections,
this.activedItem = "",
this.hasFirstSection = false,
this.onSwitchTitle,
});
@override
State<StatefulWidget> createState() => _TOCListState();
}
class _TOCListState extends ReactiveState<TOCList> {
class _TOCListState extends State<TOCList> {
String _activedItem = "";
String _lastSelectedItem = "";
final ItemScrollController _itemScrollController = ItemScrollController();
@override
Widget render(BuildContext context) {
return Container();
void didUpdateWidget(covariant TOCList oldWidget) {
super.didUpdateWidget(oldWidget);
if (_activedItem != widget.activedItem && _activedItem != _lastSelectedItem) {
_activedItem = widget.activedItem;
// Scroll to current item
int activedIndex;
if (_activedItem == "_firstSection") {
activedIndex = 0;
} else {
activedIndex = widget.sections.indexWhere((element) => element.anchor == _activedItem);
if (widget.hasFirstSection) {
activedIndex += 1;
}
if (activedIndex == -1) {
activedIndex = 0;
}
}
_itemScrollController.jumpTo(index: activedIndex);
}
}
void handleTOCItemPressed(String anchor) {
_lastSelectedItem = anchor;
widget.onSwitchTitle?.call(_lastSelectedItem);
}
@override
Widget build(BuildContext context) {
final itemCount = widget.hasFirstSection ? widget.sections.length + 1 : widget.sections.length;
return DefaultTextStyle(
style: CupertinoTheme.of(context).textTheme.textStyle,
child: Container(
color: Styles.themePageBackgroundColor,
child: SafeAreaBuilder(
builder: (context, padding) => ScrollablePositionedList.builder(
itemScrollController: _itemScrollController,
padding: EdgeInsets.only(
top: padding.top + 2,
bottom: padding.bottom + 2,
right: padding.right,
),
itemCount: itemCount,
itemBuilder: (context, index) {
if (widget.hasFirstSection && index == 0) {
//
return TOCItem(
text: "简介",
anchor: "_firstSection",
active: widget.activedItem == "_firstSection",
onPressed: handleTOCItemPressed,
);
}
var section =
widget.hasFirstSection ? widget.sections[index - 1] : widget.sections[index];
return Container(
decoration: BoxDecoration(
border: Border(
top: index == 0
? BorderSide.none
: const BorderSide(color: Color.fromRGBO(234, 236, 240, 1))),
),
child: TOCItem(
text: section.line,
number: section.number,
anchor: section.anchor,
active: widget.activedItem == section.anchor,
onPressed: handleTOCItemPressed,
),
);
},
),
),
),
);
}
}
class TOCItem extends StatelessWidget {
final VoidCallback? onTap;
final void Function(String anchor)? onPressed;
final bool active;
final String? number;
final String anchor;
final String text;
const TOCItem(
{super.key,
this.onPressed,
this.active = false,
this.number,
required this.text,
required this.anchor});
const TOCItem({super.key, this.onTap, this.active = false});
void handleTap() {
onPressed?.call(anchor);
}
@override
Widget build(BuildContext context) {
return Container();
return Stack(
children: [
ClickableBuilder(
builder: (context, mode, child) => GestureDetector(
onTap: handleTap,
child: Container(
color: mode == PointerActiveMode.active
? Styles.themeNormalActionActiveColor
: Styles.themeNormalActionColor,
padding: const EdgeInsets.only(top: 12, right: 10, bottom: 12, left: 15),
child: child,
),
),
child: DefaultTextStyle(
style: CupertinoTheme.of(context).textTheme.textStyle,
child: Row(
children: [
if (number != null) Text(number!),
if (number != null) const SizedBox(width: 10),
Flexible(
child: Text(
text,
overflow: TextOverflow.fade,
maxLines: 1,
softWrap: false,
),
),
],
),
),
),
if (active)
Positioned(
top: 0,
left: 0,
bottom: 0,
width: 4,
child: Container(
color: const Color.fromRGBO(51, 102, 204, 1),
),
),
],
);
}
}

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -19,6 +21,7 @@ class WikiPageParserController extends GetxController {
static SimpleTemplate? renderTemplate;
static String jsBridge = "";
Function(String anchor)? onSectionChange;
InAppWebViewController? webviewCotroller;
ScrollController scrollController = ScrollController();
@ -88,8 +91,7 @@ class WikiPageParserController extends GetxController {
tagList.add('<link rel="stylesheet" href="$uri" />');
}
if (parseInfo.value?.modulestyles != null &&
parseInfo.value!.modulestyles.isNotEmpty) {
if (parseInfo.value?.modulestyles != null && parseInfo.value!.modulestyles.isNotEmpty) {
var pageModules = parseInfo.value!.modulestyles
.where((item) => !Global.siteConfig.moduleStyles.contains(item));
@ -178,8 +180,7 @@ class WikiPageParserController extends GetxController {
return;
}
var anchor = args[0] as String;
debugPrint("sectionChange: $anchor");
onSectionChange?.call(anchor);
},
);
@ -216,8 +217,7 @@ navigator.safeArea = {
try {
var bytes = await rootBundle.load("assets/${uri.path}");
var data = bytes.buffer.asUint8List();
var mimeType = MimeTypeResolver()
.lookup(uri.path, headerBytes: data.sublist(0, 20));
var mimeType = MimeTypeResolver().lookup(uri.path, headerBytes: data.sublist(0, 20));
CustomSchemeResponse(
data: data,
contentType: mimeType ?? "text/plain",
@ -231,15 +231,30 @@ navigator.safeArea = {
}
void onScrollChanged(InAppWebViewController controller, int x, int y) {}
void scrollToAnchor(String anchor) {
String encodedAnchor = jsonEncode(anchor);
webviewCotroller?.evaluateJavascript(
source: "window.MugenApp && window.MugenApp.scrollToTitle($encodedAnchor)");
}
}
class WikiPageParser extends StatefulWidget {
final PageInfo? pageInfo;
final MWParseInfo? parseInfo;
final EdgeInsets? padding;
const WikiPageParser(
{super.key, this.pageInfo, this.parseInfo, this.padding});
final Function(String anchor)? onSectionChange;
final Function(WikiPageParserController controller)? ref;
const WikiPageParser({
super.key,
this.pageInfo,
this.parseInfo,
this.padding,
this.onSectionChange,
this.ref,
});
@override
State<StatefulWidget> createState() {
@ -262,8 +277,9 @@ class _WikiParserState extends ReactiveState<WikiPageParser> {
c.parseInfo.value = widget.parseInfo;
c.contentHtml.value = widget.parseInfo?.text ?? "";
c.safeAreaPadding.value = widget.padding ?? const EdgeInsets.all(0);
c.textZoom.value =
(MediaQuery.of(Get.context!).textScaleFactor * 100).round();
c.textZoom.value = (MediaQuery.of(Get.context!).textScaleFactor * 100).round();
c.onSectionChange = widget.onSectionChange;
widget.ref?.call(c);
}
Widget _buildRender() {
@ -328,7 +344,6 @@ class _WikiParserState extends ReactiveState<WikiPageParser> {
@override
Widget build(BuildContext context) {
var sc = Get.find<AppSettingsController>();
return Obx(
() => sc.betaPageRender.value ? _buildRender() : _buildWebview());
return Obx(() => sc.betaPageRender.value ? _buildRender() : _buildWebview());
}
}

@ -10,6 +10,7 @@ import 'package:isekai_wiki/api/response/parse.dart';
import 'package:isekai_wiki/components/bottom_nav_bar.dart';
import 'package:isekai_wiki/components/nav_bar_button.dart';
import 'package:isekai_wiki/components/safearea_builder.dart';
import 'package:isekai_wiki/components/toc.dart';
import 'package:isekai_wiki/components/wikipage_parser.dart';
import 'package:isekai_wiki/models/favorite_list.dart';
import 'package:isekai_wiki/reactive/reactive.dart';
@ -25,11 +26,7 @@ class MinimumArticleData {
final String? mainCategory;
final DateTime? updateTime;
MinimumArticleData(
{required this.title,
this.description,
this.mainCategory,
this.updateTime});
MinimumArticleData({required this.title, this.description, this.mainCategory, this.updateTime});
}
class ArticleCategoryData {
@ -40,6 +37,8 @@ class ArticleCategoryData {
}
class ArticlePageController extends GetxController {
WikiPageParserController? parserController;
var loading = true.obs;
var menuSlider = GlobalKey<SliderDrawerState>();
@ -52,6 +51,19 @@ class ArticlePageController extends GetxController {
var displayTitle = "".obs;
var activedSection = "_firstSection".obs;
bool get hasFirstSection {
if (parseInfo.value == null) return false;
if (parseInfo.value!.sections.isEmpty) return false;
return parseInfo.value!.sections.first.byteoffset == 0;
}
@override
void onInit() {
super.onInit();
}
Future<void> loadPageContent() async {
if (pageTitle.isNotEmpty || pageId.value != 0) {
try {
@ -59,11 +71,9 @@ class ArticlePageController extends GetxController {
loading.value = true;
MWResponse<List<PageInfo>> pageInfoRes;
if (pageId.value != 0) {
pageInfoRes =
await MWApiList.getPageInfoList(pageids: [pageId.value]);
pageInfoRes = await MWApiList.getPageInfoList(pageids: [pageId.value]);
} else {
pageInfoRes =
await MWApiList.getPageInfoList(titles: [pageTitle.value]);
pageInfoRes = await MWApiList.getPageInfoList(titles: [pageTitle.value]);
}
if (pageInfoRes.data.isEmpty) {
throw MWApiErrorException(code: 'no-page', info: "页面信息丢失");
@ -78,8 +88,7 @@ class ArticlePageController extends GetxController {
var parseRes = await MWApiParse.parse(pageId: pageId.value);
parseInfo.value = parseRes.data;
} catch (err, stack) {
alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err),
title: "错误");
alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), title: "错误");
if (kDebugMode) {
print("Exception in page: $err");
stack.printError();
@ -97,6 +106,20 @@ class ArticlePageController extends GetxController {
void handleMenuButtonClick() {
menuSlider.currentState?.openSlider();
}
void handleSwitchTitle(String anchor) {
menuSlider.currentState?.closeSlider();
Future.delayed(const Duration(milliseconds: 360)).then((_) {
activedSection.value = anchor;
});
parserController?.scrollToAnchor(anchor);
}
void handleSectionChange(String anchor) {
activedSection.value = anchor;
}
}
class ArticlePage extends StatefulWidget {
@ -104,8 +127,7 @@ class ArticlePage extends StatefulWidget {
final String? targetPage;
final int? targetPageId;
const ArticlePage(
{super.key, this.targetPage, this.targetPageId, this.initialArticleData});
const ArticlePage({super.key, this.targetPage, this.targetPageId, this.initialArticleData});
@override
State<StatefulWidget> createState() => _ArticlePageState();
@ -137,6 +159,17 @@ class _ArticlePageState extends ReactiveState<ArticlePage> {
c.displayTitle.value = widget.initialArticleData?.title ?? "加载中";
}
Widget renderTOC(BuildContext context) {
return Obx(
() => TOCList(
sections: c.parseInfo.value?.sections ?? const [],
activedItem: c.activedSection.value,
hasFirstSection: c.hasFirstSection,
onSwitchTitle: c.handleSwitchTitle,
),
);
}
@override
Widget render(BuildContext context) {
final flc = Get.find<FavoriteListController>();
@ -147,9 +180,8 @@ class _ArticlePageState extends ReactiveState<ArticlePage> {
appBar: null,
showCover: true,
slideDirection: SlideDirection.RIGHT_TO_LEFT,
slider: Container(
color: CupertinoColors.systemRed,
),
slider: renderTOC(context),
animationDuration: 350,
child: IsekaiPageScaffold(
navigationBar: IsekaiNavigationBar(
middle: Obx(() => Text(c.displayTitle.value)),
@ -167,8 +199,7 @@ class _ArticlePageState extends ReactiveState<ArticlePage> {
semanticLabel: "讨论",
),
Obx(() {
if (c.pageInfo.value != null &&
flc.isFavorite(c.pageInfo.value!)) {
if (c.pageInfo.value != null && flc.isFavorite(c.pageInfo.value!)) {
return NavBarButton(
icon: CupertinoIcons.heart_fill,
onPressed: () {},
@ -207,6 +238,10 @@ class _ArticlePageState extends ReactiveState<ArticlePage> {
padding: padding,
pageInfo: c.pageInfo.value,
parseInfo: c.parseInfo.value,
onSectionChange: c.handleSectionChange,
ref: (controller) {
c.parserController = controller;
},
),
),
),

@ -22,8 +22,7 @@ const Color _kDefaultTabBarBorderColor = CupertinoDynamicColor.withBrightness(
enum HomeTabs { newest, followed }
class HomeController extends GetxController
with GetSingleTickerProviderStateMixin {
class HomeController extends GetxController with GetSingleTickerProviderStateMixin {
double _navSearchButtonOffset = 90;
var showNavSearchButton = false.obs;
@ -42,15 +41,12 @@ class HomeController extends GetxController
void onInit() {
tabController = TabController(length: 2, vsync: this);
_navSearchButtonOffset =
48 * MediaQuery.of(Get.context!).textScaleFactor + 48;
_navSearchButtonOffset = 48 * MediaQuery.of(Get.context!).textScaleFactor + 48;
scrollController.addListener(() {
if (scrollController.offset >= _navSearchButtonOffset &&
!showNavSearchButton.value) {
if (scrollController.offset >= _navSearchButtonOffset && !showNavSearchButton.value) {
showNavSearchButton.value = true;
} else if (scrollController.offset < _navSearchButtonOffset &&
showNavSearchButton.value) {
} else if (scrollController.offset < _navSearchButtonOffset && showNavSearchButton.value) {
showNavSearchButton.value = false;
}
});
@ -108,8 +104,7 @@ class HomeTab extends StatelessWidget {
duration: const Duration(milliseconds: 100),
child: CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.bell,
size: 26, color: Styles.themeNavTitleColor),
child: const Icon(CupertinoIcons.bell, size: 26, color: Styles.themeNavTitleColor),
onPressed: () {},
),
),
@ -125,8 +120,7 @@ class HomeTab extends StatelessWidget {
duration: const Duration(milliseconds: 100),
child: CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.search,
size: 26, color: Styles.themeNavTitleColor),
child: const Icon(CupertinoIcons.search, size: 26, color: Styles.themeNavTitleColor),
onPressed: () {
onSearchClick?.call();
},
@ -152,8 +146,7 @@ class HomeTab extends StatelessWidget {
leading: _buildSearchIconButton(),
backgroundColor: Styles.themeMainColor,
brightness: Brightness.dark,
largeTitle: const Text('首页',
style: TextStyle(color: Styles.themeNavTitleColor)),
largeTitle: const Text('首页', style: TextStyle(color: Styles.themeNavTitleColor)),
trailing: _buildNotificationIconButton(),
border: Border.all(style: BorderStyle.none),
),
@ -173,8 +166,7 @@ class HomeTab extends StatelessWidget {
),
],
),
padding:
const EdgeInsets.only(bottom: 10, left: 12, right: 12),
padding: const EdgeInsets.only(bottom: 10, left: 12, right: 12),
child: CupertinoButton(
color: Colors.white,
padding: const EdgeInsets.all(0),
@ -187,12 +179,10 @@ class HomeTab extends StatelessWidget {
children: [
Container(
padding: const EdgeInsets.all(1),
child: const Icon(CupertinoIcons.search,
color: Colors.black54),
child: const Icon(CupertinoIcons.search, color: Colors.black54),
),
const Text("搜索页面...",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.black54))
textAlign: TextAlign.center, style: TextStyle(color: Colors.black54))
],
),
),
@ -223,10 +213,7 @@ class HomeTab extends StatelessWidget {
indicatorColor: Styles.themeMainColor,
labelColor: Styles.themeMainColor,
unselectedLabelColor: Colors.black45,
tabs: const [
CollapsedTabText('最新'),
CollapsedTabText('关注')
],
tabs: const [CollapsedTabText('最新'), CollapsedTabText('关注')],
onTap: (int selected) {},
),
),
@ -243,13 +230,12 @@ class HomeTab extends StatelessWidget {
maxHeight: 1,
child: Container(
decoration: BoxDecoration(
color: Colors.transparent,
color: Theme.of(context).shadowColor,
boxShadow: [
BoxShadow(
color: Theme.of(context).shadowColor,
spreadRadius: 2,
spreadRadius: 5,
blurRadius: 4,
offset: const Offset(0, 2),
)
],
),
@ -292,8 +278,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
double get maxExtent => max(maxHeight, minHeight);
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return SizedBox.expand(child: child);
}

@ -8,20 +8,7 @@ abstract class Styles {
static const double defaultFontSize = 16;
static const CupertinoTextThemeData defaultTextTheme = CupertinoTextThemeData(
textStyle: TextStyle(
fontSize: defaultFontSize,
fontFamilyFallback: [
"PingFang SC",
"Heiti SC",
"Noto Sans SC",
"Microsoft YaHei",
"Hei",
"SimHei",
],
color: CupertinoColors.label,
),
);
static const CupertinoTextThemeData defaultTextTheme = CupertinoTextThemeData();
static CupertinoThemeData cupertinoTheme = const CupertinoThemeData(
primaryColor: themeMainColor,
@ -57,8 +44,7 @@ abstract class Styles {
static const TextStyle listTileLargeTitle = TextStyle(fontSize: 18);
static const TextStyle listTileSubTitle = TextStyle(fontSize: 16);
static const TextStyle loadingDialogTitle =
TextStyle(fontSize: 18, fontWeight: FontWeight.w500);
static const TextStyle loadingDialogTitle = TextStyle(fontSize: 18, fontWeight: FontWeight.w500);
static const Color websiteNavbarColor = Color.fromRGBO(33, 37, 41, 1);
@ -70,18 +56,26 @@ abstract class Styles {
darkColor: Color.fromRGBO(41, 41, 41, 1),
);
static const Color themePageBackgroundColor =
CupertinoDynamicColor.withBrightness(
static const Color themePageBackgroundColor = CupertinoDynamicColor.withBrightness(
color: Color.fromRGBO(240, 240, 240, 1),
darkColor: Color.fromRGBO(0, 0, 0, 1),
);
static const Color themePureBackgroundColor =
CupertinoDynamicColor.withBrightness(
static const Color themePureBackgroundColor = CupertinoDynamicColor.withBrightness(
color: Color.fromRGBO(255, 255, 255, 1),
darkColor: Color.fromRGBO(0, 0, 0, 1),
);
static const Color themeNormalActionColor = CupertinoDynamicColor.withBrightness(
color: Color.fromRGBO(255, 255, 255, 1),
darkColor: Color.fromRGBO(41, 41, 41, 1),
);
static const Color themeNormalActionActiveColor = CupertinoDynamicColor.withBrightness(
color: Color.fromRGBO(204, 204, 204, 1),
darkColor: Color.fromRGBO(0, 0, 0, 1),
);
static const Color themeNavSegmentTextColor = Color.fromRGBO(12, 12, 12, 1);
static const Color linkColor = CupertinoColors.link;
static const double largeTitleFontSize = 32;

@ -777,6 +777,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.0"
scrollable_positioned_list:
dependency: "direct main"
description:
name: scrollable_positioned_list
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.5"
shelf:
dependency: transitive
description:

@ -47,6 +47,7 @@ dependencies:
like_button: ^2.0.4
skeletons: ^0.0.3
scroll_bottom_navigation_bar: ^4.0.0
scrollable_positioned_list: ^0.3.5
modal_bottom_sheet: ^2.1.2
fluttertoast: ^8.1.2
animated_snack_bar: ^0.3.0

Loading…
Cancel
Save