generated from dellevin/template
新增毛玻璃主题
This commit is contained in:
@@ -22,6 +22,8 @@ import 'services/usage_stats_service.dart';
|
|||||||
import 'services/sync/backup_service.dart';
|
import 'services/sync/backup_service.dart';
|
||||||
import 'providers/app_provider.dart';
|
import 'providers/app_provider.dart';
|
||||||
import 'widgets/app_shell.dart';
|
import 'widgets/app_shell.dart';
|
||||||
|
import 'widgets/frosted_background.dart';
|
||||||
|
import './widgets/app_overlay.dart';
|
||||||
|
|
||||||
final RouteObserver<ModalRoute<void>> routeObserver = RouteObserver<ModalRoute<void>>();
|
final RouteObserver<ModalRoute<void>> routeObserver = RouteObserver<ModalRoute<void>>();
|
||||||
|
|
||||||
@@ -234,7 +236,7 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
void _showUpdateDialog(BuildContext context, String version, List<String> features, String localVersion) {
|
void _showUpdateDialog(BuildContext context, String version, List<String> features, String localVersion) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -381,12 +383,15 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
return Consumer<AppProvider>(
|
return Consumer<AppProvider>(
|
||||||
builder: (context, provider, _) {
|
builder: (context, provider, _) {
|
||||||
AppTheme.setFontFamily(provider.fontFamily);
|
AppTheme.setFontFamily(provider.fontFamily);
|
||||||
|
final isFrosted = provider.frostedActive;
|
||||||
final light = AppTheme.getLightTheme(
|
final light = AppTheme.getLightTheme(
|
||||||
provider.colorSchemeIndex,
|
provider.colorSchemeIndex,
|
||||||
monetColor: monetColor,
|
monetColor: monetColor,
|
||||||
);
|
);
|
||||||
ThemeData dark;
|
ThemeData dark;
|
||||||
if (provider.colorSchemeIndex == -1 && monetColor != null) {
|
if (isFrosted) {
|
||||||
|
dark = AppTheme.frostedTheme;
|
||||||
|
} else if (provider.colorSchemeIndex == -1 && monetColor != null) {
|
||||||
final scheme = ColorScheme.fromSeed(
|
final scheme = ColorScheme.fromSeed(
|
||||||
seedColor: monetColor,
|
seedColor: monetColor,
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
@@ -419,9 +424,19 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: light,
|
theme: light,
|
||||||
darkTheme: dark,
|
darkTheme: dark,
|
||||||
themeMode: provider.themeMode,
|
themeMode: isFrosted ? ThemeMode.dark : provider.themeMode,
|
||||||
builder: (ctx, nav) {
|
builder: (ctx, nav) {
|
||||||
Widget shell = AppShell(child: nav!);
|
Widget shell = AppShell(child: nav!);
|
||||||
|
// 毛玻璃主题:全局背景层(模糊封面 + 深色遮罩)垫底
|
||||||
|
if (isFrosted) {
|
||||||
|
shell = Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
FrostedBackground(coverPath: provider.frostedCoverPath),
|
||||||
|
shell,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
// Android: 点击空白区域收起键盘
|
// Android: 点击空白区域收起键盘
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
shell = GestureDetector(
|
shell = GestureDetector(
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import '../../models/data_models.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../utils/image_path_helper.dart';
|
import '../../utils/image_path_helper.dart';
|
||||||
import '../../widgets/genre_selector_page.dart';
|
import '../../widgets/genre_selector_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
class BookAddPage extends StatefulWidget {
|
class BookAddPage extends StatefulWidget {
|
||||||
final VoidCallback? onCancel;
|
final VoidCallback? onCancel;
|
||||||
@@ -271,7 +272,7 @@ class _BookAddPageState extends State<BookAddPage> {
|
|||||||
|
|
||||||
void _showCoverOptions() {
|
void _showCoverOptions() {
|
||||||
final c = Theme.of(context).colorScheme;
|
final c = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(context: context, backgroundColor: c.surface,
|
appModalBottomSheet(context: context, backgroundColor: c.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
builder: (ctx) => SafeArea(child: Padding(padding: const EdgeInsets.symmetric(vertical: 16),
|
builder: (ctx) => SafeArea(child: Padding(padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
@@ -301,7 +302,7 @@ class _BookAddPageState extends State<BookAddPage> {
|
|||||||
|
|
||||||
Future<void> _pickCoverFromUrl() async {
|
Future<void> _pickCoverFromUrl() async {
|
||||||
final ctrl = TextEditingController();
|
final ctrl = TextEditingController();
|
||||||
final ok = await showDialog<bool>(context: context, builder: (ctx) {
|
final ok = await appDialog<bool>(context: context, builder: (ctx) {
|
||||||
final c = Theme.of(ctx).colorScheme;
|
final c = Theme.of(ctx).colorScheme;
|
||||||
return AlertDialog(backgroundColor: c.surface, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
return AlertDialog(backgroundColor: c.surface, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
title: Text('添加网络图片', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: c.onSurface)),
|
title: Text('添加网络图片', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: c.onSurface)),
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import '../character/character_list_page.dart';
|
|||||||
import '../../data/epub/reader_dao.dart';
|
import '../../data/epub/reader_dao.dart';
|
||||||
import '../epub_reader/epub_highlights_page.dart';
|
import '../epub_reader/epub_highlights_page.dart';
|
||||||
import '../epub_reader/reader_screen.dart';
|
import '../epub_reader/reader_screen.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 书籍详情页 - 极简主义设计
|
/// 书籍详情页 - 极简主义设计
|
||||||
class BookDetailPage extends StatefulWidget {
|
class BookDetailPage extends StatefulWidget {
|
||||||
@@ -627,7 +628,7 @@ class _BookDetailPageState extends State<BookDetailPage> {
|
|||||||
|
|
||||||
void _showEditCoverOptions() {
|
void _showEditCoverOptions() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(context: context, backgroundColor: colors.surface,
|
appModalBottomSheet(context: context, backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
builder: (ctx) => SafeArea(child: Padding(padding: const EdgeInsets.symmetric(vertical: 16),
|
builder: (ctx) => SafeArea(child: Padding(padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
@@ -661,7 +662,7 @@ class _BookDetailPageState extends State<BookDetailPage> {
|
|||||||
|
|
||||||
Future<void> _pickEditCoverFromUrl() async {
|
Future<void> _pickEditCoverFromUrl() async {
|
||||||
final urlCtrl = TextEditingController();
|
final urlCtrl = TextEditingController();
|
||||||
final confirmed = await showDialog<bool>(context: context, builder: (ctx) {
|
final confirmed = await appDialog<bool>(context: context, builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
return AlertDialog(backgroundColor: colors.surface, elevation: 0,
|
return AlertDialog(backgroundColor: colors.surface, elevation: 0,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
@@ -1312,7 +1313,7 @@ class _BookDetailPageState extends State<BookDetailPage> {
|
|||||||
const names = ['默认样式', '毛玻璃层叠'];
|
const names = ['默认样式', '毛玻璃层叠'];
|
||||||
const icons = [Icons.article_outlined, Icons.blur_on_outlined];
|
const icons = [Icons.article_outlined, Icons.blur_on_outlined];
|
||||||
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片'];
|
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片'];
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -2173,7 +2174,7 @@ class _BookDetailPageState extends State<BookDetailPage> {
|
|||||||
|
|
||||||
void _showDeleteDialog(BuildContext context) {
|
void _showDeleteDialog(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import '../../utils/toast_util.dart';
|
|||||||
import '../../data/epub/reader_dao.dart';
|
import '../../data/epub/reader_dao.dart';
|
||||||
import '../../widgets/fade_in_local_image.dart';
|
import '../../widgets/fade_in_local_image.dart';
|
||||||
import 'book_excerpt_form_page.dart';
|
import 'book_excerpt_form_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 书籍摘抄列表页面
|
/// 书籍摘抄列表页面
|
||||||
class BookExcerptsPage extends StatefulWidget {
|
class BookExcerptsPage extends StatefulWidget {
|
||||||
@@ -363,7 +364,7 @@ class _BookExcerptsPageState extends State<BookExcerptsPage> {
|
|||||||
|
|
||||||
Future<bool?> _showDeleteDialog(BookExcerpt excerpt) {
|
Future<bool?> _showDeleteDialog(BookExcerpt excerpt) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
return showDialog<bool>(
|
return appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
@@ -407,7 +408,7 @@ class _BookExcerptsPageState extends State<BookExcerptsPage> {
|
|||||||
|
|
||||||
void _showDeleteAllDialog() {
|
void _showDeleteAllDialog() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
@@ -449,7 +450,7 @@ class _BookExcerptsPageState extends State<BookExcerptsPage> {
|
|||||||
|
|
||||||
void _showImportSheet() {
|
void _showImportSheet() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
@@ -532,7 +533,7 @@ class _BookExcerptsPageState extends State<BookExcerptsPage> {
|
|||||||
void _showWechatReadImport() {
|
void _showWechatReadImport() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import '../../utils/image_path_helper.dart';
|
|||||||
import '../../widgets/genre_selector_page.dart';
|
import '../../widgets/genre_selector_page.dart';
|
||||||
import '../../widgets/text_input_panel.dart';
|
import '../../widgets/text_input_panel.dart';
|
||||||
import '../../widgets/alternate_titles_dialog.dart';
|
import '../../widgets/alternate_titles_dialog.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 从多值字段列表中提取去重排序的唯一值(供 compute 使用)
|
/// 从多值字段列表中提取去重排序的唯一值(供 compute 使用)
|
||||||
List<String> _collectUnique(List<List<String>> lists) {
|
List<String> _collectUnique(List<List<String>> lists) {
|
||||||
@@ -394,7 +395,7 @@ class _BookFormPageState extends State<BookFormPage> {
|
|||||||
|
|
||||||
Future<void> _pickCover() async {
|
Future<void> _pickCover() async {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
await showModalBottomSheet(
|
await appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -447,7 +448,7 @@ class _BookFormPageState extends State<BookFormPage> {
|
|||||||
|
|
||||||
Future<void> _pickCoverFromUrl() async {
|
Future<void> _pickCoverFromUrl() async {
|
||||||
final urlController = TextEditingController();
|
final urlController = TextEditingController();
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final c = Theme.of(ctx).colorScheme;
|
final c = Theme.of(ctx).colorScheme;
|
||||||
@@ -567,7 +568,7 @@ class _BookFormPageState extends State<BookFormPage> {
|
|||||||
/// 编辑书名(弹窗)
|
/// 编辑书名(弹窗)
|
||||||
Future<void> _editTitle() async {
|
Future<void> _editTitle() async {
|
||||||
final controller = TextEditingController(text: _titleController.text);
|
final controller = TextEditingController(text: _titleController.text);
|
||||||
final result = await showDialog<String>(
|
final result = await appDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -615,7 +616,7 @@ class _BookFormPageState extends State<BookFormPage> {
|
|||||||
|
|
||||||
/// 编辑别名(弹窗 + 标签式输入)
|
/// 编辑别名(弹窗 + 标签式输入)
|
||||||
Future<void> _editAlternateTitles() async {
|
Future<void> _editAlternateTitles() async {
|
||||||
final result = await showDialog<List<String>>(
|
final result = await appDialog<List<String>>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlternateTitlesDialog(initial: _alternateTitles),
|
builder: (ctx) => AlternateTitlesDialog(initial: _alternateTitles),
|
||||||
);
|
);
|
||||||
@@ -643,7 +644,7 @@ class _BookFormPageState extends State<BookFormPage> {
|
|||||||
|
|
||||||
Future<void> _editReadCount() async {
|
Future<void> _editReadCount() async {
|
||||||
final controller = TextEditingController(text: _readCount > 0 ? '$_readCount' : '');
|
final controller = TextEditingController(text: _readCount > 0 ? '$_readCount' : '');
|
||||||
final result = await showDialog<String>(
|
final result = await appDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: const Text('阅读次数'),
|
title: const Text('阅读次数'),
|
||||||
@@ -689,7 +690,7 @@ class _BookFormPageState extends State<BookFormPage> {
|
|||||||
Future<bool> _confirmLeave() async {
|
Future<bool> _confirmLeave() async {
|
||||||
if (!_hasContent()) return true;
|
if (!_hasContent()) return true;
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final result = await showDialog<bool>(
|
final result = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
backgroundColor: colors.surface, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import '../../models/data_models.dart';
|
|||||||
import '../../providers/app_provider.dart';
|
import '../../providers/app_provider.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import 'book_review_form_page.dart';
|
import 'book_review_form_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 书评详情页
|
/// 书评详情页
|
||||||
class BookReviewDetailPage extends StatefulWidget {
|
class BookReviewDetailPage extends StatefulWidget {
|
||||||
@@ -174,7 +175,7 @@ class _BookReviewDetailPageState extends State<BookReviewDetailPage> {
|
|||||||
|
|
||||||
Future<void> _deleteReview() async {
|
Future<void> _deleteReview() async {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import '../../models/data_models.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import 'book_review_form_page.dart';
|
import 'book_review_form_page.dart';
|
||||||
import 'book_review_detail_page.dart';
|
import 'book_review_detail_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 书籍书评列表页面
|
/// 书籍书评列表页面
|
||||||
class BookReviewsPage extends StatefulWidget {
|
class BookReviewsPage extends StatefulWidget {
|
||||||
@@ -286,7 +287,7 @@ class _BookReviewsPageState extends State<BookReviewsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showDeleteDialog(BookReview review) {
|
void _showDeleteDialog(BookReview review) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import '../../widgets/master_detail_scaffold.dart';
|
|||||||
import '../../widgets/detail_placeholder.dart';
|
import '../../widgets/detail_placeholder.dart';
|
||||||
import 'book_detail_page.dart';
|
import 'book_detail_page.dart';
|
||||||
import 'book_add_page.dart';
|
import 'book_add_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 状态索引 → 状态值
|
/// 状态索引 → 状态值
|
||||||
const _bookStatusMap = {0: 'read', 1: 'reading', 2: 'want_to_read', 3: 'abandoned'};
|
const _bookStatusMap = {0: 'read', 1: 'reading', 2: 'want_to_read', 3: 'abandoned'};
|
||||||
@@ -412,7 +413,7 @@ class _BookTabViewState extends State<_BookTabView>
|
|||||||
|
|
||||||
void _showDeleteDialog(BuildContext context, Book book) {
|
void _showDeleteDialog(BuildContext context, Book book) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import '../../utils/image_path_helper.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../widgets/fade_in_local_image.dart';
|
import '../../widgets/fade_in_local_image.dart';
|
||||||
import '../../widgets/genre_selector_page.dart';
|
import '../../widgets/genre_selector_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 角色编辑/添加页面
|
/// 角色编辑/添加页面
|
||||||
///
|
///
|
||||||
@@ -220,7 +221,7 @@ class _CharacterFormPageState extends State<CharacterFormPage> {
|
|||||||
|
|
||||||
void _showImageOptions() {
|
void _showImageOptions() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -277,7 +278,7 @@ class _CharacterFormPageState extends State<CharacterFormPage> {
|
|||||||
|
|
||||||
Future<void> _pickImageFromUrl() async {
|
Future<void> _pickImageFromUrl() async {
|
||||||
String? url;
|
String? url;
|
||||||
final confirmed = await showDialog<bool>(context: context, builder: (ctx) {
|
final confirmed = await appDialog<bool>(context: context, builder: (ctx) {
|
||||||
final urlCtrl = TextEditingController();
|
final urlCtrl = TextEditingController();
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
@@ -424,7 +425,7 @@ class _CharacterFormPageState extends State<CharacterFormPage> {
|
|||||||
Future<bool> _confirmLeave() async {
|
Future<bool> _confirmLeave() async {
|
||||||
if (!_hasContent()) return true;
|
if (!_hasContent()) return true;
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final result = await showDialog<bool>(
|
final result = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import '../../providers/app_provider.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../widgets/fade_in_local_image.dart';
|
import '../../widgets/fade_in_local_image.dart';
|
||||||
import 'character_form_page.dart';
|
import 'character_form_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 影视角色列表页
|
/// 影视角色列表页
|
||||||
class MovieCharactersPage extends StatefulWidget {
|
class MovieCharactersPage extends StatefulWidget {
|
||||||
@@ -358,7 +359,7 @@ class _CharacterTile extends StatelessWidget {
|
|||||||
|
|
||||||
void _showDeleteDialog(BuildContext context) {
|
void _showDeleteDialog(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'dart:async';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'reader_style_sheet.dart';
|
import 'reader_style_sheet.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
class ControlPanel extends StatefulWidget {
|
class ControlPanel extends StatefulWidget {
|
||||||
final bool showControls;
|
final bool showControls;
|
||||||
@@ -217,7 +218,7 @@ class _ControlPanelState extends State<ControlPanel> {
|
|||||||
|
|
||||||
void _openStyleSheet() {
|
void _openStyleSheet() {
|
||||||
widget.onToggleStyleDrawer();
|
widget.onToggleStyleDrawer();
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import 'epub_edit_page.dart';
|
|||||||
import 'epub_highlights_page.dart';
|
import 'epub_highlights_page.dart';
|
||||||
import 'highlight_detail_sheet.dart';
|
import 'highlight_detail_sheet.dart';
|
||||||
import 'reader_screen.dart';
|
import 'reader_screen.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// EPUB 书籍详情页
|
/// EPUB 书籍详情页
|
||||||
class EpubDetailPage extends StatefulWidget {
|
class EpubDetailPage extends StatefulWidget {
|
||||||
@@ -730,7 +731,7 @@ class _EpubDetailPageState extends State<EpubDetailPage> {
|
|||||||
|
|
||||||
/// 删除摘抄(同步删除蓝色高亮)
|
/// 删除摘抄(同步删除蓝色高亮)
|
||||||
Future<void> _deleteExcerpt(BookExcerpt excerpt, ColorScheme colors) async {
|
Future<void> _deleteExcerpt(BookExcerpt excerpt, ColorScheme colors) async {
|
||||||
final confirmed = showDialog<bool>(
|
final confirmed = appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: const Text('删除摘抄', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
title: const Text('删除摘抄', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
||||||
@@ -774,7 +775,7 @@ class _EpubDetailPageState extends State<EpubDetailPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _deleteHighlight(int id, ColorScheme colors) async {
|
Future<void> _deleteHighlight(int id, ColorScheme colors) async {
|
||||||
final confirmed = showDialog<bool>(
|
final confirmed = appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: const Text('删除句读', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
title: const Text('删除句读', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
||||||
@@ -873,7 +874,7 @@ class _EpubDetailPageState extends State<EpubDetailPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showLinkedBookActions(ColorScheme colors, String title) {
|
void _showLinkedBookActions(ColorScheme colors, String title) {
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -914,7 +915,7 @@ class _EpubDetailPageState extends State<EpubDetailPage> {
|
|||||||
title: Text('取消关联', style: TextStyle(fontSize: 13, color: colors.error)),
|
title: Text('取消关联', style: TextStyle(fontSize: 13, color: colors.error)),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(ctx);
|
Navigator.pop(ctx);
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (confirmCtx) => AlertDialog(
|
builder: (confirmCtx) => AlertDialog(
|
||||||
title: const Text('取消关联', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
title: const Text('取消关联', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import '../../utils/toast_util.dart';
|
|||||||
import '../../utils/user_prefs.dart';
|
import '../../utils/user_prefs.dart';
|
||||||
import 'highlight_detail_sheet.dart';
|
import 'highlight_detail_sheet.dart';
|
||||||
import 'reader_screen.dart';
|
import 'reader_screen.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// EPUB 句读(高亮)管理页面 —— 支持瀑布流/列表模式切换
|
/// EPUB 句读(高亮)管理页面 —— 支持瀑布流/列表模式切换
|
||||||
class EpubHighlightsPage extends StatefulWidget {
|
class EpubHighlightsPage extends StatefulWidget {
|
||||||
@@ -317,7 +318,7 @@ class _EpubHighlightsPageState extends State<EpubHighlightsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showDeleteConfirm(int id, ColorScheme colors) {
|
void _showDeleteConfirm(int id, ColorScheme colors) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import '../../utils/toast_util.dart';
|
|||||||
import '../../widgets/fade_in_local_image.dart';
|
import '../../widgets/fade_in_local_image.dart';
|
||||||
import '../../widgets/shimmer_skeleton.dart';
|
import '../../widgets/shimmer_skeleton.dart';
|
||||||
import 'epub_detail_page.dart';
|
import 'epub_detail_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// EPUB 书架页面
|
/// EPUB 书架页面
|
||||||
class EpubLibraryPage extends StatefulWidget {
|
class EpubLibraryPage extends StatefulWidget {
|
||||||
@@ -88,7 +89,7 @@ class _EpubLibraryPageState extends State<EpubLibraryPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (_) => const Center(child: CircularProgressIndicator()),
|
builder: (_) => const Center(child: CircularProgressIndicator()),
|
||||||
@@ -107,7 +108,7 @@ class _EpubLibraryPageState extends State<EpubLibraryPage> {
|
|||||||
|
|
||||||
Future<void> _deleteBook(Map<String, dynamic> book) async {
|
Future<void> _deleteBook(Map<String, dynamic> book) async {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final confirm = await showDialog<bool>(
|
final confirm = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -166,7 +167,7 @@ class _EpubLibraryPageState extends State<EpubLibraryPage> {
|
|||||||
(2, '按阅读进度排序', Icons.auto_stories_outlined),
|
(2, '按阅读进度排序', Icons.auto_stories_outlined),
|
||||||
(3, '按书名排序', Icons.sort_by_alpha),
|
(3, '按书名排序', Icons.sort_by_alpha),
|
||||||
];
|
];
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import 'highlight_share_page.dart';
|
import 'highlight_share_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 句读详情弹窗 —— 类似分享卡片的样式
|
/// 句读详情弹窗 —— 类似分享卡片的样式
|
||||||
/// 从 epub_detail_page 和 epub_highlights_page 共用
|
/// 从 epub_detail_page 和 epub_highlights_page 共用
|
||||||
@@ -19,7 +20,7 @@ void showHighlightDetailSheet(
|
|||||||
final createdAt = highlight['created_at'] as String? ?? '';
|
final createdAt = highlight['created_at'] as String? ?? '';
|
||||||
final bookTitle = book['title'] as String? ?? '';
|
final bookTitle = book['title'] as String? ?? '';
|
||||||
|
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
@@ -246,7 +247,7 @@ void showExcerptDetailSheet(
|
|||||||
}) {
|
}) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ mixin _LinkHandlingMixin on State<ReaderScreen> {
|
|||||||
} else if (linkHandling == ReaderLinkHandling.ask) {
|
} else if (linkHandling == ReaderLinkHandling.ask) {
|
||||||
if (mounted && context.mounted) {
|
if (mounted && context.mounted) {
|
||||||
final shouldOpen =
|
final shouldOpen =
|
||||||
await showDialog<bool>(
|
await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text('打开外部链接'),
|
title: const Text('打开外部链接'),
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import 'footnote_popup.dart';
|
|||||||
import 'search_sheet.dart';
|
import 'search_sheet.dart';
|
||||||
import 'epub_selection_toolbar.dart';
|
import 'epub_selection_toolbar.dart';
|
||||||
import 'selection_handles.dart';
|
import 'selection_handles.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
part 'mixins/spine_navigation_mixin.dart';
|
part 'mixins/spine_navigation_mixin.dart';
|
||||||
part 'mixins/page_navigation_mixin.dart';
|
part 'mixins/page_navigation_mixin.dart';
|
||||||
@@ -439,7 +440,7 @@ class _ReaderScreenState extends State<ReaderScreen>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _openSearch() {
|
void _openSearch() {
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import '../../services/epub/epub_theme.dart';
|
import '../../services/epub/epub_theme.dart';
|
||||||
import 'widgets/integer_stepper.dart';
|
import 'widgets/integer_stepper.dart';
|
||||||
import 'widgets/reader_scale_slider.dart';
|
import 'widgets/reader_scale_slider.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// Simplified reader style configuration bottom sheet.
|
/// Simplified reader style configuration bottom sheet.
|
||||||
///
|
///
|
||||||
@@ -382,7 +383,7 @@ class _ReaderStyleSheetState extends State<ReaderStyleSheet> {
|
|||||||
Color bgColor = Color(_customBgColor);
|
Color bgColor = Color(_customBgColor);
|
||||||
Color textColor = Color(_customTextColor);
|
Color textColor = Color(_customTextColor);
|
||||||
|
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
return StatefulBuilder(
|
return StatefulBuilder(
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import '../movies/movie_detail_page.dart';
|
|||||||
import '../movies/movie_form_page.dart';
|
import '../movies/movie_form_page.dart';
|
||||||
import '../book/book_detail_page.dart';
|
import '../book/book_detail_page.dart';
|
||||||
import '../book/book_form_page.dart';
|
import '../book/book_form_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 书影日历 - 按月展示影视/书籍添加记录
|
/// 书影日历 - 按月展示影视/书籍添加记录
|
||||||
class MediaCalendarPage extends StatefulWidget {
|
class MediaCalendarPage extends StatefulWidget {
|
||||||
@@ -457,7 +458,7 @@ class _MediaCalendarPageState extends State<MediaCalendarPage> {
|
|||||||
|
|
||||||
void _showAddMenu(BuildContext context) {
|
void _showAddMenu(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import '../../models/data_models.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../utils/image_path_helper.dart';
|
import '../../utils/image_path_helper.dart';
|
||||||
import '../../widgets/genre_selector_page.dart';
|
import '../../widgets/genre_selector_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
class GameAddPage extends StatefulWidget {
|
class GameAddPage extends StatefulWidget {
|
||||||
final VoidCallback? onCancel;
|
final VoidCallback? onCancel;
|
||||||
@@ -307,7 +308,7 @@ class _GameAddPageState extends State<GameAddPage> {
|
|||||||
|
|
||||||
void _showCoverOptions() {
|
void _showCoverOptions() {
|
||||||
final c = Theme.of(context).colorScheme;
|
final c = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(context: context, backgroundColor: c.surface,
|
appModalBottomSheet(context: context, backgroundColor: c.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
builder: (ctx) => SafeArea(child: Padding(padding: const EdgeInsets.symmetric(vertical: 16),
|
builder: (ctx) => SafeArea(child: Padding(padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
@@ -337,7 +338,7 @@ class _GameAddPageState extends State<GameAddPage> {
|
|||||||
|
|
||||||
Future<void> _pickCoverFromUrl() async {
|
Future<void> _pickCoverFromUrl() async {
|
||||||
final ctrl = TextEditingController();
|
final ctrl = TextEditingController();
|
||||||
final ok = await showDialog<bool>(context: context, builder: (ctx) {
|
final ok = await appDialog<bool>(context: context, builder: (ctx) {
|
||||||
final c = Theme.of(ctx).colorScheme;
|
final c = Theme.of(ctx).colorScheme;
|
||||||
return AlertDialog(backgroundColor: c.surface, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
return AlertDialog(backgroundColor: c.surface, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
title: Text('添加网络图片', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: c.onSurface)),
|
title: Text('添加网络图片', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: c.onSurface)),
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import 'game_reviews_page.dart';
|
|||||||
import 'game_screenshots_page.dart';
|
import 'game_screenshots_page.dart';
|
||||||
import 'game_share_page.dart';
|
import 'game_share_page.dart';
|
||||||
import '../character/character_list_page.dart';
|
import '../character/character_list_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 游戏详情页 - 极简主义设计
|
/// 游戏详情页 - 极简主义设计
|
||||||
class GameDetailPage extends StatefulWidget {
|
class GameDetailPage extends StatefulWidget {
|
||||||
@@ -835,7 +836,7 @@ class _GameDetailPageState extends State<GameDetailPage> {
|
|||||||
|
|
||||||
void _showEditCoverOptions() {
|
void _showEditCoverOptions() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context, backgroundColor: colors.surface,
|
context: context, backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
builder: (ctx) => SafeArea(
|
builder: (ctx) => SafeArea(
|
||||||
@@ -875,7 +876,7 @@ class _GameDetailPageState extends State<GameDetailPage> {
|
|||||||
|
|
||||||
Future<void> _pickEditCoverFromUrl() async {
|
Future<void> _pickEditCoverFromUrl() async {
|
||||||
final urlCtrl = TextEditingController();
|
final urlCtrl = TextEditingController();
|
||||||
final confirmed = await showDialog<bool>(context: context, builder: (ctx) {
|
final confirmed = await appDialog<bool>(context: context, builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
@@ -1872,7 +1873,7 @@ class _GameDetailPageState extends State<GameDetailPage> {
|
|||||||
const names = ['默认样式', '毛玻璃层叠'];
|
const names = ['默认样式', '毛玻璃层叠'];
|
||||||
const icons = [Icons.article_outlined, Icons.blur_on_outlined];
|
const icons = [Icons.article_outlined, Icons.blur_on_outlined];
|
||||||
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片'];
|
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片'];
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -1917,7 +1918,7 @@ class _GameDetailPageState extends State<GameDetailPage> {
|
|||||||
|
|
||||||
void _showDeleteDialog(BuildContext context) {
|
void _showDeleteDialog(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import '../../utils/toast_util.dart';
|
|||||||
import '../../utils/image_path_helper.dart';
|
import '../../utils/image_path_helper.dart';
|
||||||
import '../../widgets/genre_selector_page.dart';
|
import '../../widgets/genre_selector_page.dart';
|
||||||
import '../../widgets/text_input_panel.dart';
|
import '../../widgets/text_input_panel.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 从多值字段列表中提取去重排序的唯一值(供 compute 使用)
|
/// 从多值字段列表中提取去重排序的唯一值(供 compute 使用)
|
||||||
List<String> _collectUnique(List<List<String>> lists) {
|
List<String> _collectUnique(List<List<String>> lists) {
|
||||||
@@ -833,7 +834,7 @@ class _GameFormPageState extends State<GameFormPage> {
|
|||||||
/// 显示封面选择选项
|
/// 显示封面选择选项
|
||||||
void _showCoverOptions() {
|
void _showCoverOptions() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
@@ -904,7 +905,7 @@ class _GameFormPageState extends State<GameFormPage> {
|
|||||||
/// 从网络链接选择封面
|
/// 从网络链接选择封面
|
||||||
Future<void> _pickCoverFromUrl() async {
|
Future<void> _pickCoverFromUrl() async {
|
||||||
final urlController = TextEditingController();
|
final urlController = TextEditingController();
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -1001,7 +1002,7 @@ class _GameFormPageState extends State<GameFormPage> {
|
|||||||
/// 编辑游戏名称(弹窗)
|
/// 编辑游戏名称(弹窗)
|
||||||
Future<void> _editTitle() async {
|
Future<void> _editTitle() async {
|
||||||
final controller = TextEditingController(text: _titleController.text);
|
final controller = TextEditingController(text: _titleController.text);
|
||||||
final result = await showDialog<String>(
|
final result = await appDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -1049,7 +1050,7 @@ class _GameFormPageState extends State<GameFormPage> {
|
|||||||
|
|
||||||
Future<void> _editPlayCount() async {
|
Future<void> _editPlayCount() async {
|
||||||
final controller = TextEditingController(text: _playCount > 0 ? '$_playCount' : '');
|
final controller = TextEditingController(text: _playCount > 0 ? '$_playCount' : '');
|
||||||
final result = await showDialog<String>(
|
final result = await appDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: const Text('游玩次数'),
|
title: const Text('游玩次数'),
|
||||||
@@ -1076,7 +1077,7 @@ class _GameFormPageState extends State<GameFormPage> {
|
|||||||
final hoursController = TextEditingController(text: _playTimeHoursController.text);
|
final hoursController = TextEditingController(text: _playTimeHoursController.text);
|
||||||
final minutesController = TextEditingController(text: _playTimeMinutesController.text);
|
final minutesController = TextEditingController(text: _playTimeMinutesController.text);
|
||||||
|
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -1185,7 +1186,7 @@ class _GameFormPageState extends State<GameFormPage> {
|
|||||||
Future<bool> _confirmLeave() async {
|
Future<bool> _confirmLeave() async {
|
||||||
if (!_hasContent()) return true;
|
if (!_hasContent()) return true;
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final result = await showDialog<bool>(
|
final result = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import '../../providers/app_provider.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../widgets/fade_in_local_image.dart';
|
import '../../widgets/fade_in_local_image.dart';
|
||||||
import 'game_review_form_page.dart';
|
import 'game_review_form_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 游戏评价详情页
|
/// 游戏评价详情页
|
||||||
class GameReviewDetailPage extends StatefulWidget {
|
class GameReviewDetailPage extends StatefulWidget {
|
||||||
@@ -41,7 +42,7 @@ class _GameReviewDetailPageState extends State<GameReviewDetailPage> {
|
|||||||
|
|
||||||
Future<void> _deleteReview() async {
|
Future<void> _deleteReview() async {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import '../../models/data_models.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import 'game_review_form_page.dart';
|
import 'game_review_form_page.dart';
|
||||||
import 'game_review_detail_page.dart';
|
import 'game_review_detail_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 游戏评价列表页面
|
/// 游戏评价列表页面
|
||||||
class GameReviewsPage extends StatefulWidget {
|
class GameReviewsPage extends StatefulWidget {
|
||||||
@@ -224,7 +225,7 @@ class _GameReviewsPageState extends State<GameReviewsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showDeleteDialog(GameReview review) {
|
void _showDeleteDialog(GameReview review) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import '../../models/data_models.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../utils/image_path_helper.dart';
|
import '../../utils/image_path_helper.dart';
|
||||||
import 'screenshot_gallery_page.dart';
|
import 'screenshot_gallery_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 游戏截图页面
|
/// 游戏截图页面
|
||||||
class GameScreenshotsPage extends StatefulWidget {
|
class GameScreenshotsPage extends StatefulWidget {
|
||||||
@@ -147,7 +148,7 @@ class _GameScreenshotsPageState extends State<GameScreenshotsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pickScreenshot() async {
|
Future<void> _pickScreenshot() async {
|
||||||
final result = await showModalBottomSheet<int>(
|
final result = await appModalBottomSheet<int>(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -247,7 +248,7 @@ class _GameScreenshotsPageState extends State<GameScreenshotsPage> {
|
|||||||
|
|
||||||
Future<void> _pickFromUrl() async {
|
Future<void> _pickFromUrl() async {
|
||||||
final urlController = TextEditingController();
|
final urlController = TextEditingController();
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
@@ -332,7 +333,7 @@ class _GameScreenshotsPageState extends State<GameScreenshotsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showDeleteDialog(GameScreenshot screenshot) {
|
void _showDeleteDialog(GameScreenshot screenshot) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:share_plus/share_plus.dart';
|
|||||||
import '../../models/data_models.dart';
|
import '../../models/data_models.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../widgets/fade_in_local_image.dart';
|
import '../../widgets/fade_in_local_image.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
class GameSharePage extends StatefulWidget {
|
class GameSharePage extends StatefulWidget {
|
||||||
final Game game;
|
final Game game;
|
||||||
@@ -61,7 +62,7 @@ class _GameSharePageState extends State<GameSharePage> {
|
|||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
const icons = [Icons.image_outlined, Icons.sports_esports_outlined];
|
const icons = [Icons.image_outlined, Icons.sports_esports_outlined];
|
||||||
const subtitles = ['简约海报风格', '游戏信息卡风格'];
|
const subtitles = ['简约海报风格', '游戏信息卡风格'];
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import '../../widgets/master_detail_scaffold.dart';
|
|||||||
import '../../widgets/detail_placeholder.dart';
|
import '../../widgets/detail_placeholder.dart';
|
||||||
import 'game_detail_page.dart';
|
import 'game_detail_page.dart';
|
||||||
import 'game_add_page.dart';
|
import 'game_add_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 状态索引 → 状态值
|
/// 状态索引 → 状态值
|
||||||
const _gameStatusMap = {0: 'completed', 1: 'playing', 2: 'want_to_play', 3: 'abandoned'};
|
const _gameStatusMap = {0: 'completed', 1: 'playing', 2: 'want_to_play', 3: 'abandoned'};
|
||||||
@@ -519,7 +520,7 @@ class _GameTabViewState extends State<_GameTabView>
|
|||||||
|
|
||||||
void _showDeleteDialog(BuildContext context, Game game) {
|
void _showDeleteDialog(BuildContext context, Game game) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import 'main_content_page.dart';
|
|||||||
import '../online_search/search_page.dart';
|
import '../online_search/search_page.dart';
|
||||||
import '../online_search/online_search_page.dart';
|
import '../online_search/online_search_page.dart';
|
||||||
import '../profile/profile_page.dart';
|
import '../profile/profile_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 主页 - 包含底部导航,可切换主页/我的
|
/// 主页 - 包含底部导航,可切换主页/我的
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
@@ -451,7 +452,7 @@ class _DesktopIconRail extends StatelessWidget {
|
|||||||
void _showSearchDialog(BuildContext context) {
|
void _showSearchDialog(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final dialogHeight = (MediaQuery.of(context).size.height * 0.82).clamp(560.0, 820.0);
|
final dialogHeight = (MediaQuery.of(context).size.height * 0.82).clamp(560.0, 820.0);
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (dialogCtx) => Dialog(
|
builder: (dialogCtx) => Dialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -473,7 +474,7 @@ class _DesktopIconRail extends StatelessWidget {
|
|||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final email = 'dellevin99@gmail.com';
|
final email = 'dellevin99@gmail.com';
|
||||||
final qqGroup = '1087203310';
|
final qqGroup = '1087203310';
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -536,40 +537,40 @@ class _DesktopIconRail extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
void _showBackupDialog(BuildContext context) {
|
void _showBackupDialog(BuildContext context) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => const _BackupChoiceDialog(),
|
builder: (_) => const _BackupChoiceDialog(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showEncounterDialog(BuildContext context) {
|
void _showEncounterDialog(BuildContext context) {
|
||||||
showDialog(context: context, builder: (_) => const _EncounterDialog());
|
appDialog(context: context, builder: (_) => const _EncounterDialog());
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showStrollDialog(BuildContext context) {
|
void _showStrollDialog(BuildContext context) {
|
||||||
showDialog(context: context, builder: (_) => const _StrollDialog());
|
appDialog(context: context, builder: (_) => const _StrollDialog());
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showCalendarDialog(BuildContext context) {
|
void _showCalendarDialog(BuildContext context) {
|
||||||
showDialog(context: context, builder: (_) => const _CalendarDialog());
|
appDialog(context: context, builder: (_) => const _CalendarDialog());
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showPersonDialog(BuildContext context) {
|
void _showPersonDialog(BuildContext context) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => const _PersonListDialog(),
|
builder: (_) => const _PersonListDialog(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showTagDialog(BuildContext context) {
|
void _showTagDialog(BuildContext context) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => const _TagManagementDialog(),
|
builder: (_) => const _TagManagementDialog(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showRecycleBinDialog(BuildContext context) {
|
void _showRecycleBinDialog(BuildContext context) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => const _RecycleBinDialog(),
|
builder: (_) => const _RecycleBinDialog(),
|
||||||
);
|
);
|
||||||
@@ -1165,7 +1166,7 @@ class _StrollDialogState extends State<_StrollDialog> {
|
|||||||
|
|
||||||
void _showDeleteConfirm(_StrollItem item) {
|
void _showDeleteConfirm(_StrollItem item) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(context: context, builder: (ctx) => AlertDialog(
|
appDialog(context: context, builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
title: Text('确认删除', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: colors.onSurface)),
|
title: Text('确认删除', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: colors.onSurface)),
|
||||||
@@ -2058,7 +2059,7 @@ class _TagManagementDialogState extends State<_TagManagementDialog> {
|
|||||||
final name = tag['name'] as String;
|
final name = tag['name'] as String;
|
||||||
final isHidden = (tag['is_hidden'] as int?) == 1;
|
final isHidden = (tag['is_hidden'] as int?) == 1;
|
||||||
|
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
|
||||||
@@ -2127,7 +2128,7 @@ class _TagManagementDialogState extends State<_TagManagementDialog> {
|
|||||||
} else {
|
} else {
|
||||||
for (final n in provider.notes.where((n) => !n.isDeleted && n.tags.contains(tagName))) items.add((title: n.title.isNotEmpty ? n.title : '随手记', subtitle: null, type: '笔记'));
|
for (final n in provider.notes.where((n) => !n.isDeleted && n.tags.contains(tagName))) items.add((title: n.title.isNotEmpty ? n.title : '随手记', subtitle: null, type: '笔记'));
|
||||||
}
|
}
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -2161,7 +2162,7 @@ class _TagManagementDialogState extends State<_TagManagementDialog> {
|
|||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
final type = _currentType;
|
final type = _currentType;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
backgroundColor: colors.surface, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
@@ -2208,7 +2209,7 @@ class _TagManagementDialogState extends State<_TagManagementDialog> {
|
|||||||
final tagId = tag['id'] as String;
|
final tagId = tag['id'] as String;
|
||||||
final type = tag['type'] as String;
|
final type = tag['type'] as String;
|
||||||
final oldName = tag['name'] as String;
|
final oldName = tag['name'] as String;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
backgroundColor: colors.surface, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
@@ -2250,7 +2251,7 @@ class _TagManagementDialogState extends State<_TagManagementDialog> {
|
|||||||
bool showAdvanced = false;
|
bool showAdvanced = false;
|
||||||
final otherTags = (_tagCache[type] ?? []).where((t) => t['id'] != tagId).map((t) => t['name'] as String).toList();
|
final otherTags = (_tagCache[type] ?? []).where((t) => t['id'] != tagId).map((t) => t['name'] as String).toList();
|
||||||
|
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => StatefulBuilder(builder: (ctx, setDialogState) {
|
builder: (ctx) => StatefulBuilder(builder: (ctx, setDialogState) {
|
||||||
final bc = Theme.of(ctx).colorScheme;
|
final bc = Theme.of(ctx).colorScheme;
|
||||||
@@ -2625,7 +2626,7 @@ class _LocalBackupContentState extends State<_LocalBackupContent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _importData() async {
|
Future<void> _importData() async {
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -2820,7 +2821,7 @@ class _WebDAVBackupContentState extends State<_WebDAVBackupContent> {
|
|||||||
|
|
||||||
Future<void> _confirmSync(SyncDirection direction) async {
|
Future<void> _confirmSync(SyncDirection direction) async {
|
||||||
final isUpload = direction == SyncDirection.upload;
|
final isUpload = direction == SyncDirection.upload;
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -2844,7 +2845,7 @@ class _WebDAVBackupContentState extends State<_WebDAVBackupContent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _clearConfig() async {
|
Future<void> _clearConfig() async {
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -3343,7 +3344,7 @@ class _RecycleBinDialogState extends State<_RecycleBinDialog> {
|
|||||||
|
|
||||||
Future<void> _permanentDelete(_BinItem item) async {
|
Future<void> _permanentDelete(_BinItem item) async {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
@@ -3382,7 +3383,7 @@ class _RecycleBinDialogState extends State<_RecycleBinDialog> {
|
|||||||
|
|
||||||
void _showClearAllDialog() {
|
void _showClearAllDialog() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
@@ -4007,7 +4008,7 @@ class _DesktopListPanelState extends State<_DesktopListPanel> {
|
|||||||
|
|
||||||
Future<void> _deleteItem(BuildContext context, String type, String id, String title) async {
|
Future<void> _deleteItem(BuildContext context, String type, String id, String title) async {
|
||||||
final provider = context.read<AppProvider>();
|
final provider = context.read<AppProvider>();
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: Theme.of(ctx).colorScheme.surface,
|
backgroundColor: Theme.of(ctx).colorScheme.surface,
|
||||||
@@ -4463,7 +4464,7 @@ class _DesktopNoteItem extends StatelessWidget {
|
|||||||
// 进入编辑模式由 NoteDetailPage 处理
|
// 进入编辑模式由 NoteDetailPage 处理
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: Theme.of(ctx).colorScheme.surface,
|
backgroundColor: Theme.of(ctx).colorScheme.surface,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import '../note/note_tab_page.dart';
|
|||||||
import '../game/game_tab_page.dart';
|
import '../game/game_tab_page.dart';
|
||||||
import '../online_search/search_hub_page.dart';
|
import '../online_search/search_hub_page.dart';
|
||||||
import '../sync/webdav_sync_page.dart';
|
import '../sync/webdav_sync_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 主内容页 - 观影/阅读/笔记标签页(PageView 滑动切换)
|
/// 主内容页 - 观影/阅读/笔记标签页(PageView 滑动切换)
|
||||||
class MainContentPage extends StatefulWidget {
|
class MainContentPage extends StatefulWidget {
|
||||||
@@ -149,7 +150,7 @@ class _MainContentPageState extends State<MainContentPage> {
|
|||||||
void _showModulePicker(BuildContext context, List<_TabItem> tabs, int currentIndex) {
|
void _showModulePicker(BuildContext context, List<_TabItem> tabs, int currentIndex) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final provider = context.read<AppProvider>();
|
final provider = context.read<AppProvider>();
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -211,7 +212,7 @@ class _MainContentPageState extends State<MainContentPage> {
|
|||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final hasConfig = (await WebDAVService.instance.getConfig()) != null;
|
final hasConfig = (await WebDAVService.instance.getConfig()) != null;
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
|
||||||
@@ -331,7 +332,7 @@ class _MainContentPageState extends State<MainContentPage> {
|
|||||||
|
|
||||||
void _showSortMenu(BuildContext context, String title, int current, List<(int, String, IconData)> options, ValueChanged<int> onSelected) {
|
void _showSortMenu(BuildContext context, String title, int current, List<(int, String, IconData)> options, ValueChanged<int> onSelected) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -663,7 +664,7 @@ class _CloudSheetContentState extends State<_CloudSheetContent> {
|
|||||||
// 使用保存的 navigator context,而非 state context(bottom sheet 已 pop)
|
// 使用保存的 navigator context,而非 state context(bottom sheet 已 pop)
|
||||||
final overlayCtx = navigator.context;
|
final overlayCtx = navigator.context;
|
||||||
final colors = Theme.of(overlayCtx).colorScheme;
|
final colors = Theme.of(overlayCtx).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: overlayCtx,
|
context: overlayCtx,
|
||||||
builder: (dialogCtx) => AlertDialog(
|
builder: (dialogCtx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:path/path.dart' as p;
|
|||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import '../../utils/user_prefs.dart';
|
import '../../utils/user_prefs.dart';
|
||||||
import 'md_viewer_page.dart';
|
import 'md_viewer_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// Markdown 阅读器 - 文件浏览器
|
/// Markdown 阅读器 - 文件浏览器
|
||||||
class MdReaderTabPage extends StatefulWidget {
|
class MdReaderTabPage extends StatefulWidget {
|
||||||
@@ -88,7 +89,7 @@ class _MdReaderTabPageState extends State<MdReaderTabPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showPermissionDeniedDialog() {
|
void _showPermissionDeniedDialog() {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -276,7 +277,7 @@ class _MdReaderTabPageState extends State<MdReaderTabPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showSettingsSheet() {
|
void _showSettingsSheet() {
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 豆瓣影视WebView页面 - 用于抓取影视信息
|
/// 豆瓣影视WebView页面 - 用于抓取影视信息
|
||||||
class DoubanWebViewPage extends StatefulWidget {
|
class DoubanWebViewPage extends StatefulWidget {
|
||||||
@@ -139,7 +140,7 @@ class _DoubanWebViewPageState extends State<DoubanWebViewPage> {
|
|||||||
|
|
||||||
// 显示提取的信息
|
// 显示提取的信息
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -242,7 +243,7 @@ class _DoubanWebViewPageState extends State<DoubanWebViewPage> {
|
|||||||
_isExtracting = true;
|
_isExtracting = true;
|
||||||
|
|
||||||
// 显示加载提示
|
// 显示加载提示
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (dialogContext) => const Center(
|
builder: (dialogContext) => const Center(
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import '../../models/data_models.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../utils/image_path_helper.dart';
|
import '../../utils/image_path_helper.dart';
|
||||||
import '../../widgets/genre_selector_page.dart';
|
import '../../widgets/genre_selector_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
class MovieAddPage extends StatefulWidget {
|
class MovieAddPage extends StatefulWidget {
|
||||||
final VoidCallback? onCancel;
|
final VoidCallback? onCancel;
|
||||||
@@ -281,7 +282,7 @@ class _MovieAddPageState extends State<MovieAddPage> {
|
|||||||
|
|
||||||
void _showCoverOptions() {
|
void _showCoverOptions() {
|
||||||
final c = Theme.of(context).colorScheme;
|
final c = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(context: context, backgroundColor: c.surface,
|
appModalBottomSheet(context: context, backgroundColor: c.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
builder: (ctx) => SafeArea(child: Padding(padding: const EdgeInsets.symmetric(vertical: 16),
|
builder: (ctx) => SafeArea(child: Padding(padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
@@ -311,7 +312,7 @@ class _MovieAddPageState extends State<MovieAddPage> {
|
|||||||
|
|
||||||
Future<void> _pickCoverFromUrl() async {
|
Future<void> _pickCoverFromUrl() async {
|
||||||
final ctrl = TextEditingController();
|
final ctrl = TextEditingController();
|
||||||
final ok = await showDialog<bool>(context: context, builder: (ctx) {
|
final ok = await appDialog<bool>(context: context, builder: (ctx) {
|
||||||
final c = Theme.of(ctx).colorScheme;
|
final c = Theme.of(ctx).colorScheme;
|
||||||
return AlertDialog(backgroundColor: c.surface, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
return AlertDialog(backgroundColor: c.surface, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
title: Text('添加网络图片', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: c.onSurface)),
|
title: Text('添加网络图片', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: c.onSurface)),
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import 'movie_reviews_page.dart';
|
|||||||
import 'movie_posters_page.dart';
|
import 'movie_posters_page.dart';
|
||||||
import 'movie_share_page.dart';
|
import 'movie_share_page.dart';
|
||||||
import '../character/character_list_page.dart';
|
import '../character/character_list_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 影视详情页 - 极简主义设计
|
/// 影视详情页 - 极简主义设计
|
||||||
class MovieDetailPage extends StatefulWidget {
|
class MovieDetailPage extends StatefulWidget {
|
||||||
@@ -786,7 +787,7 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
|
|||||||
|
|
||||||
void _showEditCoverOptions() {
|
void _showEditCoverOptions() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context, backgroundColor: colors.surface,
|
context: context, backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
builder: (ctx) => SafeArea(
|
builder: (ctx) => SafeArea(
|
||||||
@@ -826,7 +827,7 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
|
|||||||
|
|
||||||
Future<void> _pickEditCoverFromUrl() async {
|
Future<void> _pickEditCoverFromUrl() async {
|
||||||
final urlCtrl = TextEditingController();
|
final urlCtrl = TextEditingController();
|
||||||
final confirmed = await showDialog<bool>(context: context, builder: (ctx) {
|
final confirmed = await appDialog<bool>(context: context, builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
@@ -1197,7 +1198,7 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
|
|||||||
const names = ['默认样式', '毛玻璃层叠'];
|
const names = ['默认样式', '毛玻璃层叠'];
|
||||||
const icons = [Icons.article_outlined, Icons.blur_on_outlined];
|
const icons = [Icons.article_outlined, Icons.blur_on_outlined];
|
||||||
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片'];
|
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片'];
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -2103,7 +2104,7 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
|
|||||||
|
|
||||||
void _showDeleteDialog(BuildContext context) {
|
void _showDeleteDialog(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import '../../widgets/genre_selector_page.dart';
|
|||||||
import '../../widgets/text_input_panel.dart';
|
import '../../widgets/text_input_panel.dart';
|
||||||
import '../../widgets/duration_picker.dart';
|
import '../../widgets/duration_picker.dart';
|
||||||
import '../../widgets/alternate_titles_dialog.dart';
|
import '../../widgets/alternate_titles_dialog.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 从多值字段列表中提取去重排序的唯一值(供 compute 使用)
|
/// 从多值字段列表中提取去重排序的唯一值(供 compute 使用)
|
||||||
List<String> _collectUnique(List<List<String>> lists) {
|
List<String> _collectUnique(List<List<String>> lists) {
|
||||||
@@ -147,7 +148,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
|||||||
void _showQuickAddDialog() {
|
void _showQuickAddDialog() {
|
||||||
final textController = TextEditingController();
|
final textController = TextEditingController();
|
||||||
|
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (dialogContext) {
|
builder: (dialogContext) {
|
||||||
final colors = Theme.of(dialogContext).colorScheme;
|
final colors = Theme.of(dialogContext).colorScheme;
|
||||||
@@ -748,7 +749,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
|||||||
/// 编辑名称(弹窗)
|
/// 编辑名称(弹窗)
|
||||||
Future<void> _editTitle() async {
|
Future<void> _editTitle() async {
|
||||||
final controller = TextEditingController(text: _titleController.text);
|
final controller = TextEditingController(text: _titleController.text);
|
||||||
final result = await showDialog<String>(
|
final result = await appDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -796,7 +797,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
|||||||
|
|
||||||
/// 编辑别名(弹窗 + 标签式输入)
|
/// 编辑别名(弹窗 + 标签式输入)
|
||||||
Future<void> _editAlternateTitles() async {
|
Future<void> _editAlternateTitles() async {
|
||||||
final result = await showDialog<List<String>>(
|
final result = await appDialog<List<String>>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlternateTitlesDialog(initial: _alternateTitles),
|
builder: (ctx) => AlternateTitlesDialog(initial: _alternateTitles),
|
||||||
);
|
);
|
||||||
@@ -807,7 +808,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
|||||||
/// 编辑观看次数
|
/// 编辑观看次数
|
||||||
Future<void> _editWatchCount() async {
|
Future<void> _editWatchCount() async {
|
||||||
final controller = TextEditingController(text: _watchCount > 0 ? '$_watchCount' : '');
|
final controller = TextEditingController(text: _watchCount > 0 ? '$_watchCount' : '');
|
||||||
final result = await showDialog<String>(
|
final result = await appDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: const Text('观看次数'),
|
title: const Text('观看次数'),
|
||||||
@@ -1118,7 +1119,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
|||||||
/// 显示封面选择选项
|
/// 显示封面选择选项
|
||||||
void _showCoverOptions() {
|
void _showCoverOptions() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
@@ -1292,7 +1293,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
|||||||
Future<void> _pickCoverFromUrl() async {
|
Future<void> _pickCoverFromUrl() async {
|
||||||
final urlController = TextEditingController();
|
final urlController = TextEditingController();
|
||||||
|
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -1400,7 +1401,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
|
|||||||
Future<bool> _confirmLeave() async {
|
Future<bool> _confirmLeave() async {
|
||||||
if (!_hasContent()) return true;
|
if (!_hasContent()) return true;
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final result = await showDialog<bool>(
|
final result = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import '../../models/data_models.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../utils/image_path_helper.dart';
|
import '../../utils/image_path_helper.dart';
|
||||||
import 'poster_gallery_page.dart';
|
import 'poster_gallery_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 影视海报墙页面
|
/// 影视海报墙页面
|
||||||
class MoviePostersPage extends StatefulWidget {
|
class MoviePostersPage extends StatefulWidget {
|
||||||
@@ -184,7 +185,7 @@ class _MoviePostersPageState extends State<MoviePostersPage> {
|
|||||||
|
|
||||||
Future<void> _pickPoster() async {
|
Future<void> _pickPoster() async {
|
||||||
// 显示选择对话框
|
// 显示选择对话框
|
||||||
final result = await showModalBottomSheet<int>(
|
final result = await appModalBottomSheet<int>(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
@@ -311,7 +312,7 @@ class _MoviePostersPageState extends State<MoviePostersPage> {
|
|||||||
Future<void> _pickFromUrl() async {
|
Future<void> _pickFromUrl() async {
|
||||||
final urlController = TextEditingController();
|
final urlController = TextEditingController();
|
||||||
|
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
@@ -504,7 +505,7 @@ class _MoviePostersPageState extends State<MoviePostersPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showDeleteDialog(MoviePoster poster) {
|
void _showDeleteDialog(MoviePoster poster) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import '../../providers/app_provider.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../widgets/fade_in_local_image.dart';
|
import '../../widgets/fade_in_local_image.dart';
|
||||||
import 'movie_review_form_page.dart';
|
import 'movie_review_form_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 影评详情页
|
/// 影评详情页
|
||||||
class MovieReviewDetailPage extends StatefulWidget {
|
class MovieReviewDetailPage extends StatefulWidget {
|
||||||
@@ -45,7 +46,7 @@ class _MovieReviewDetailPageState extends State<MovieReviewDetailPage> {
|
|||||||
|
|
||||||
Future<void> _deleteReview() async {
|
Future<void> _deleteReview() async {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import '../../models/data_models.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import 'movie_review_form_page.dart';
|
import 'movie_review_form_page.dart';
|
||||||
import 'movie_review_detail_page.dart';
|
import 'movie_review_detail_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 影视影评列表页面
|
/// 影视影评列表页面
|
||||||
class MovieReviewsPage extends StatefulWidget {
|
class MovieReviewsPage extends StatefulWidget {
|
||||||
@@ -286,7 +287,7 @@ class _MovieReviewsPageState extends State<MovieReviewsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showDeleteDialog(MovieReview review) {
|
void _showDeleteDialog(MovieReview review) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:share_plus/share_plus.dart';
|
|||||||
import '../../models/data_models.dart';
|
import '../../models/data_models.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../widgets/fade_in_local_image.dart';
|
import '../../widgets/fade_in_local_image.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
class MovieSharePage extends StatefulWidget {
|
class MovieSharePage extends StatefulWidget {
|
||||||
final Movie movie;
|
final Movie movie;
|
||||||
@@ -128,7 +129,7 @@ class _MovieSharePageState extends State<MovieSharePage> {
|
|||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
const icons = [Icons.image_outlined, Icons.confirmation_num_outlined, Icons.local_movies_outlined, Icons.card_giftcard_outlined];
|
const icons = [Icons.image_outlined, Icons.confirmation_num_outlined, Icons.local_movies_outlined, Icons.card_giftcard_outlined];
|
||||||
const subtitles = ['简约海报风格', '经典复古票根', '电影票票根样式', '高端收藏版票根'];
|
const subtitles = ['简约海报风格', '经典复古票根', '电影票票根样式', '高端收藏版票根'];
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -185,7 +186,7 @@ class _MovieSharePageState extends State<MovieSharePage> {
|
|||||||
|
|
||||||
void _showEditSheet() {
|
void _showEditSheet() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import '../../widgets/master_detail_scaffold.dart';
|
|||||||
import '../../widgets/detail_placeholder.dart';
|
import '../../widgets/detail_placeholder.dart';
|
||||||
import 'movie_detail_page.dart';
|
import 'movie_detail_page.dart';
|
||||||
import 'movie_add_page.dart';
|
import 'movie_add_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 状态索引 → 状态值
|
/// 状态索引 → 状态值
|
||||||
const _statusMap = {0: 'watched', 1: 'watching', 2: 'want_to_watch'};
|
const _statusMap = {0: 'watched', 1: 'watching', 2: 'want_to_watch'};
|
||||||
@@ -530,7 +531,7 @@ class _MovieTabViewState extends State<_MovieTabView>
|
|||||||
|
|
||||||
void _showDeleteDialog(BuildContext context, Movie movie) {
|
void _showDeleteDialog(BuildContext context, Movie movie) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import '../../utils/responsive.dart';
|
|||||||
import '../../widgets/vditor_editor.dart';
|
import '../../widgets/vditor_editor.dart';
|
||||||
import '../../widgets/tag_side_panel.dart';
|
import '../../widgets/tag_side_panel.dart';
|
||||||
import 'note_share_page.dart';
|
import 'note_share_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 笔记详情页
|
/// 笔记详情页
|
||||||
class NoteDetailPage extends StatefulWidget {
|
class NoteDetailPage extends StatefulWidget {
|
||||||
@@ -804,7 +805,7 @@ class _NoteDetailPageState extends State<NoteDetailPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showImagePreview(List<String> images, int initialIndex) {
|
void _showImagePreview(List<String> images, int initialIndex) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
builder: (context) => GestureDetector(
|
builder: (context) => GestureDetector(
|
||||||
@@ -920,7 +921,7 @@ class _NoteDetailPageState extends State<NoteDetailPage> {
|
|||||||
|
|
||||||
void _showDeleteDialog(BuildContext context) {
|
void _showDeleteDialog(BuildContext context) {
|
||||||
final errorColor = Theme.of(context).colorScheme.error;
|
final errorColor = Theme.of(context).colorScheme.error;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: const Text('删除笔记'),
|
title: const Text('删除笔记'),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import '../../utils/image_saver.dart';
|
|||||||
import '../../widgets/fade_in_local_image.dart';
|
import '../../widgets/fade_in_local_image.dart';
|
||||||
import '../../widgets/tag_side_panel.dart';
|
import '../../widgets/tag_side_panel.dart';
|
||||||
import '../../widgets/vditor_editor.dart';
|
import '../../widgets/vditor_editor.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 添加/编辑笔记页面 - 极简书写界面
|
/// 添加/编辑笔记页面 - 极简书写界面
|
||||||
class NoteFormPage extends StatefulWidget {
|
class NoteFormPage extends StatefulWidget {
|
||||||
@@ -625,7 +626,7 @@ class _NoteFormPageState extends State<NoteFormPage> {
|
|||||||
_autoSaveTimer?.cancel();
|
_autoSaveTimer?.cancel();
|
||||||
if (!_hasContent()) return true;
|
if (!_hasContent()) return true;
|
||||||
|
|
||||||
final result = await showDialog<String>(
|
final result = await appDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -899,7 +900,7 @@ class _NoteFormPageState extends State<NoteFormPage> {
|
|||||||
|
|
||||||
/// 显示图片预览
|
/// 显示图片预览
|
||||||
void _showImagePreview(int index) {
|
void _showImagePreview(int index) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
builder: (context) => GestureDetector(
|
builder: (context) => GestureDetector(
|
||||||
@@ -926,7 +927,7 @@ class _NoteFormPageState extends State<NoteFormPage> {
|
|||||||
|
|
||||||
/// 显示删除图片确认对话框
|
/// 显示删除图片确认对话框
|
||||||
void _showDeleteImageDialog(int index) {
|
void _showDeleteImageDialog(int index) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import '../../widgets/master_detail_scaffold.dart';
|
|||||||
import '../../widgets/detail_placeholder.dart';
|
import '../../widgets/detail_placeholder.dart';
|
||||||
import 'note_detail_page.dart';
|
import 'note_detail_page.dart';
|
||||||
import 'note_add_page.dart';
|
import 'note_add_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 笔记标签页(分页 + 触底加载)
|
/// 笔记标签页(分页 + 触底加载)
|
||||||
class NoteTabPage extends StatefulWidget {
|
class NoteTabPage extends StatefulWidget {
|
||||||
@@ -355,7 +356,7 @@ class _NoteTabPageState extends State<NoteTabPage> {
|
|||||||
|
|
||||||
void _showNoteActions(Note note) {
|
void _showNoteActions(Note note) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -398,7 +399,7 @@ class _NoteTabPageState extends State<NoteTabPage> {
|
|||||||
|
|
||||||
void _showDeleteDialog(Note note) {
|
void _showDeleteDialog(Note note) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import '../../models/data_models.dart';
|
|||||||
import '../../providers/app_provider.dart';
|
import '../../providers/app_provider.dart';
|
||||||
import '../../utils/image_path_helper.dart';
|
import '../../utils/image_path_helper.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 书籍详情页 - 在线版
|
/// 书籍详情页 - 在线版
|
||||||
class BookDetailPage extends StatefulWidget {
|
class BookDetailPage extends StatefulWidget {
|
||||||
@@ -227,7 +228,7 @@ class _BookDetailPageState extends State<BookDetailPage> {
|
|||||||
|
|
||||||
void _showAddSheet() {
|
void _showAddSheet() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import '../../models/data_models.dart';
|
|||||||
import '../../providers/app_provider.dart';
|
import '../../providers/app_provider.dart';
|
||||||
import '../../utils/image_path_helper.dart';
|
import '../../utils/image_path_helper.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 影视详情页 - 在线版
|
/// 影视详情页 - 在线版
|
||||||
class MovieDetailPage extends StatefulWidget {
|
class MovieDetailPage extends StatefulWidget {
|
||||||
@@ -318,7 +319,7 @@ class _MovieDetailPageState extends State<MovieDetailPage>
|
|||||||
Future<void> _showRateSheet() async {
|
Future<void> _showRateSheet() async {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final rates = [0.5, 0.75, 1.0, 1.25, 1.5, 2.0];
|
final rates = [0.5, 0.75, 1.0, 1.25, 1.5, 2.0];
|
||||||
final selected = await showModalBottomSheet<double>(
|
final selected = await appModalBottomSheet<double>(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
@@ -547,7 +548,7 @@ class _MovieDetailPageState extends State<MovieDetailPage>
|
|||||||
|
|
||||||
void _showAddSheet() {
|
void _showAddSheet() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import '../../utils/user_prefs.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import 'movie_detail_page.dart';
|
import 'movie_detail_page.dart';
|
||||||
import 'book_detail_page.dart';
|
import 'book_detail_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 在线搜索影视/书籍(带 Scaffold + AppBar)
|
/// 在线搜索影视/书籍(带 Scaffold + AppBar)
|
||||||
class OnlineSearchPage extends StatelessWidget {
|
class OnlineSearchPage extends StatelessWidget {
|
||||||
@@ -143,7 +144,7 @@ class _OnlineSearchPageBodyState extends State<OnlineSearchPageBody> {
|
|||||||
|
|
||||||
void _showUnlockDialog() {
|
void _showUnlockDialog() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -499,7 +500,7 @@ class _OnlineSearchPageBodyState extends State<OnlineSearchPageBody> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showTokenExpiredDialog() {
|
void _showTokenExpiredDialog() {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final c = Theme.of(ctx).colorScheme;
|
final c = Theme.of(ctx).colorScheme;
|
||||||
@@ -982,7 +983,7 @@ class _OnlineSearchPageBodyState extends State<OnlineSearchPageBody> {
|
|||||||
const Spacer(),
|
const Spacer(),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final c = Theme.of(ctx).colorScheme;
|
final c = Theme.of(ctx).colorScheme;
|
||||||
@@ -1057,7 +1058,7 @@ class _OnlineSearchPageBodyState extends State<OnlineSearchPageBody> {
|
|||||||
_doSearch();
|
_doSearch();
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final c = Theme.of(ctx).colorScheme;
|
final c = Theme.of(ctx).colorScheme;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import '../movies/movie_detail_page.dart';
|
|||||||
import '../book/book_detail_page.dart';
|
import '../book/book_detail_page.dart';
|
||||||
import '../game/game_detail_page.dart';
|
import '../game/game_detail_page.dart';
|
||||||
import 'person_form_page.dart';
|
import 'person_form_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 人物档案详情页
|
/// 人物档案详情页
|
||||||
class PersonDetailPage extends StatefulWidget {
|
class PersonDetailPage extends StatefulWidget {
|
||||||
@@ -464,7 +465,7 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
|
|||||||
|
|
||||||
void _showDeleteDialog(Person person) {
|
void _showDeleteDialog(Person person) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import '../../utils/image_path_helper.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../widgets/fade_in_local_image.dart';
|
import '../../widgets/fade_in_local_image.dart';
|
||||||
import '../../widgets/genre_selector_page.dart';
|
import '../../widgets/genre_selector_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 人物编辑/添加页面
|
/// 人物编辑/添加页面
|
||||||
class PersonFormPage extends StatefulWidget {
|
class PersonFormPage extends StatefulWidget {
|
||||||
@@ -259,7 +260,7 @@ class _PersonFormPageState extends State<PersonFormPage> {
|
|||||||
|
|
||||||
void _showPhotoOptions() {
|
void _showPhotoOptions() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -311,7 +312,7 @@ class _PersonFormPageState extends State<PersonFormPage> {
|
|||||||
|
|
||||||
Future<void> _pickPhotoFromUrl() async {
|
Future<void> _pickPhotoFromUrl() async {
|
||||||
String? url;
|
String? url;
|
||||||
final confirmed = await showDialog<bool>(context: context, builder: (ctx) {
|
final confirmed = await appDialog<bool>(context: context, builder: (ctx) {
|
||||||
final urlCtrl = TextEditingController();
|
final urlCtrl = TextEditingController();
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
@@ -503,7 +504,7 @@ class _PersonFormPageState extends State<PersonFormPage> {
|
|||||||
Future<bool> _confirmLeave() async {
|
Future<bool> _confirmLeave() async {
|
||||||
if (!_hasContent()) return true;
|
if (!_hasContent()) return true;
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final result = await showDialog<bool>(
|
final result = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface, elevation: 0,
|
backgroundColor: colors.surface, elevation: 0,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import '../../utils/toast_util.dart';
|
|||||||
import '../../widgets/person_avatar.dart';
|
import '../../widgets/person_avatar.dart';
|
||||||
import 'person_detail_page.dart';
|
import 'person_detail_page.dart';
|
||||||
import 'person_form_page.dart';
|
import 'person_form_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 人物列表页
|
/// 人物列表页
|
||||||
class PersonListPage extends StatefulWidget {
|
class PersonListPage extends StatefulWidget {
|
||||||
@@ -526,7 +527,7 @@ class _PersonListPageState extends State<PersonListPage> {
|
|||||||
Future<void> _refreshRelations() async {
|
Future<void> _refreshRelations() async {
|
||||||
final provider = context.read<AppProvider>();
|
final provider = context.read<AppProvider>();
|
||||||
// 显示加载弹窗
|
// 显示加载弹窗
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (ctx) => PopScope(
|
builder: (ctx) => PopScope(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import '../../utils/user_prefs.dart';
|
|||||||
import '../../widgets/fade_in_local_image.dart';
|
import '../../widgets/fade_in_local_image.dart';
|
||||||
import 'playlist_create_page.dart';
|
import 'playlist_create_page.dart';
|
||||||
import 'playlist_detail_page.dart';
|
import 'playlist_detail_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
class PlaylistListPage extends StatefulWidget {
|
class PlaylistListPage extends StatefulWidget {
|
||||||
const PlaylistListPage({super.key});
|
const PlaylistListPage({super.key});
|
||||||
@@ -561,7 +562,7 @@ class _PlaylistListPageState extends State<PlaylistListPage> {
|
|||||||
|
|
||||||
void _showActionSheet(BuildContext context, Playlist playlist, AppProvider provider) {
|
void _showActionSheet(BuildContext context, Playlist playlist, AppProvider provider) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -642,7 +643,7 @@ class _PlaylistListPageState extends State<PlaylistListPage> {
|
|||||||
|
|
||||||
Future<bool?> _showDeleteDialog(BuildContext context, Playlist playlist, AppProvider provider) {
|
Future<bool?> _showDeleteDialog(BuildContext context, Playlist playlist, AppProvider provider) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
return showDialog<bool>(
|
return appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import '../../services/changelog_service.dart';
|
import '../../services/changelog_service.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 更新日志页面
|
/// 更新日志页面
|
||||||
class ChangelogPage extends StatefulWidget {
|
class ChangelogPage extends StatefulWidget {
|
||||||
@@ -55,7 +56,7 @@ class _ChangelogPageState extends State<ChangelogPage> {
|
|||||||
|
|
||||||
void _showNoUpdateDialog(String localVersion) {
|
void _showNoUpdateDialog(String localVersion) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -77,7 +78,7 @@ class _ChangelogPageState extends State<ChangelogPage> {
|
|||||||
|
|
||||||
void _showUpdateDialog({required String version, String? localVersion}) {
|
void _showUpdateDialog({required String version, String? localVersion}) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:io';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../../utils/user_prefs.dart';
|
import '../../utils/user_prefs.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
class FeatureSettingsPage extends StatefulWidget {
|
class FeatureSettingsPage extends StatefulWidget {
|
||||||
const FeatureSettingsPage({super.key});
|
const FeatureSettingsPage({super.key});
|
||||||
@@ -442,7 +443,7 @@ class _FeatureSettingsPageState extends State<FeatureSettingsPage> {
|
|||||||
void _showDefaultTabPicker() {
|
void _showDefaultTabPicker() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final enabled = _enabledTabs;
|
final enabled = _enabledTabs;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
builder: (ctx) => Container(
|
builder: (ctx) => Container(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:permission_handler/permission_handler.dart';
|
|||||||
import '../../services/font_download_manager.dart';
|
import '../../services/font_download_manager.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../utils/user_prefs.dart';
|
import '../../utils/user_prefs.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 字体选择页面
|
/// 字体选择页面
|
||||||
///
|
///
|
||||||
@@ -59,7 +60,7 @@ class _FontPickerPageState extends State<FontPickerPage> {
|
|||||||
/// 显示权限提示弹窗
|
/// 显示权限提示弹窗
|
||||||
void _showPermissionDialog() {
|
void _showPermissionDialog() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../../providers/app_provider.dart';
|
import '../../providers/app_provider.dart';
|
||||||
import '../../utils/user_prefs.dart';
|
import '../../utils/user_prefs.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
class LayoutSettingsPage extends StatefulWidget {
|
class LayoutSettingsPage extends StatefulWidget {
|
||||||
const LayoutSettingsPage({super.key});
|
const LayoutSettingsPage({super.key});
|
||||||
@@ -337,7 +338,7 @@ class _LayoutSettingsPageState extends State<LayoutSettingsPage> {
|
|||||||
|
|
||||||
void _showHomeModuleSheet() {
|
void _showHomeModuleSheet() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -380,7 +381,7 @@ class _LayoutSettingsPageState extends State<LayoutSettingsPage> {
|
|||||||
|
|
||||||
void _showMovieSheet() {
|
void _showMovieSheet() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -445,7 +446,7 @@ class _LayoutSettingsPageState extends State<LayoutSettingsPage> {
|
|||||||
|
|
||||||
void _showBookSheet() {
|
void _showBookSheet() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -502,7 +503,7 @@ class _LayoutSettingsPageState extends State<LayoutSettingsPage> {
|
|||||||
|
|
||||||
void _showNoteSheet() {
|
void _showNoteSheet() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -538,7 +539,7 @@ class _LayoutSettingsPageState extends State<LayoutSettingsPage> {
|
|||||||
|
|
||||||
void _showGameSheet() {
|
void _showGameSheet() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import '../explore/stroll_page.dart';
|
|||||||
import '../sync/cloud_sync_page.dart';
|
import '../sync/cloud_sync_page.dart';
|
||||||
import 'settings_page.dart';
|
import 'settings_page.dart';
|
||||||
import 'watchlist_page.dart';
|
import 'watchlist_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 个人中心页面
|
/// 个人中心页面
|
||||||
class ProfilePage extends StatefulWidget {
|
class ProfilePage extends StatefulWidget {
|
||||||
@@ -1053,7 +1054,7 @@ class _ProfilePageState extends State<ProfilePage> with RouteAware {
|
|||||||
void _showFeedbackDialog(BuildContext context) {
|
void _showFeedbackDialog(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final email = 'dellevin99@gmail.com';
|
final email = 'dellevin99@gmail.com';
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
@@ -1210,7 +1211,7 @@ class _ProfilePageState extends State<ProfilePage> with RouteAware {
|
|||||||
|
|
||||||
Future<void> _pickAvatar() async {
|
Future<void> _pickAvatar() async {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
await showModalBottomSheet(
|
await appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
@@ -1312,7 +1313,7 @@ class _ProfilePageState extends State<ProfilePage> with RouteAware {
|
|||||||
|
|
||||||
void _showBackupOptions(BuildContext context) {
|
void _showBackupOptions(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
@@ -1414,7 +1415,7 @@ class _ProfilePageState extends State<ProfilePage> with RouteAware {
|
|||||||
}
|
}
|
||||||
options.add(('笔记', Icons.sticky_note_2_outlined, provider.notes.where((n) => !n.isDeleted).length));
|
options.add(('笔记', Icons.sticky_note_2_outlined, provider.notes.where((n) => !n.isDeleted).length));
|
||||||
|
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
@@ -1515,7 +1516,7 @@ class _ProfilePageState extends State<ProfilePage> with RouteAware {
|
|||||||
|
|
||||||
void _showExportResult(BuildContext context, bool success, String message) {
|
void _showExportResult(BuildContext context, bool success, String message) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import 'feature_settings_page.dart';
|
|||||||
import 'layout_settings_page.dart';
|
import 'layout_settings_page.dart';
|
||||||
import 'changelog_page.dart';
|
import 'changelog_page.dart';
|
||||||
import 'font_picker_page.dart';
|
import 'font_picker_page.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 设置页面
|
/// 设置页面
|
||||||
class SettingsPage extends StatefulWidget {
|
class SettingsPage extends StatefulWidget {
|
||||||
@@ -263,7 +264,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final nicknameController = TextEditingController(text: _userPrefs.nickname);
|
final nicknameController = TextEditingController(text: _userPrefs.nickname);
|
||||||
final mottoController = TextEditingController(text: _userPrefs.motto);
|
final mottoController = TextEditingController(text: _userPrefs.motto);
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -351,11 +352,12 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const _themeModeLabels = ['跟随系统', '浅色模式', '深色模式'];
|
static const _themeModeLabels = ['跟随系统', '浅色模式', '深色模式', '毛玻璃'];
|
||||||
static const _themeModeIcons = [
|
static const _themeModeIcons = [
|
||||||
Icons.brightness_auto,
|
Icons.brightness_auto,
|
||||||
Icons.light_mode,
|
Icons.light_mode,
|
||||||
Icons.dark_mode
|
Icons.dark_mode,
|
||||||
|
Icons.blur_on
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildThemeModeSelector() {
|
Widget _buildThemeModeSelector() {
|
||||||
@@ -401,7 +403,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
|
|
||||||
void _showThemeModePicker() {
|
void _showThemeModePicker() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
builder: (ctx) => Container(
|
builder: (ctx) => Container(
|
||||||
@@ -464,6 +466,13 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
|
|
||||||
Future<void> _setThemeMode(int mode) async {
|
Future<void> _setThemeMode(int mode) async {
|
||||||
await _userPrefs.setThemeMode(mode);
|
await _userPrefs.setThemeMode(mode);
|
||||||
|
if (mode == 3) {
|
||||||
|
if (mounted) {
|
||||||
|
context.read<AppProvider>().enableFrosted();
|
||||||
|
setState(() => _themeMode = mode);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
final themeMode = switch (mode) {
|
final themeMode = switch (mode) {
|
||||||
1 => ThemeMode.light,
|
1 => ThemeMode.light,
|
||||||
2 => ThemeMode.dark,
|
2 => ThemeMode.dark,
|
||||||
@@ -527,7 +536,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
void _showColorSchemePicker() {
|
void _showColorSchemePicker() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final provider = context.read<AppProvider>();
|
final provider = context.read<AppProvider>();
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
builder: (ctx) => Container(
|
builder: (ctx) => Container(
|
||||||
@@ -904,7 +913,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
|
|
||||||
void _showStoragePermissionDialog() {
|
void _showStoragePermissionDialog() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -945,7 +954,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
void _showClearCacheDialog(BuildContext pageContext) async {
|
void _showClearCacheDialog(BuildContext pageContext) async {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
// 先扫描分析
|
// 先扫描分析
|
||||||
showDialog(
|
appDialog(
|
||||||
context: pageContext,
|
context: pageContext,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (_) => Center(child: CircularProgressIndicator(color: colors.primary)),
|
builder: (_) => Center(child: CircularProgressIndicator(color: colors.primary)),
|
||||||
@@ -968,7 +977,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
showDialog(
|
appDialog(
|
||||||
context: pageContext,
|
context: pageContext,
|
||||||
builder: (dialogContext) => AlertDialog(
|
builder: (dialogContext) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -1038,7 +1047,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
|
|
||||||
Future<void> _clearCacheData(BuildContext context) async {
|
Future<void> _clearCacheData(BuildContext context) async {
|
||||||
try {
|
try {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (_) => const Center(child: CircularProgressIndicator()));
|
builder: (_) => const Center(child: CircularProgressIndicator()));
|
||||||
@@ -1058,7 +1067,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
void _showCacheResult(
|
void _showCacheResult(
|
||||||
BuildContext context, bool success, String message, bool cleaned) {
|
BuildContext context, bool success, String message, bool cleaned) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
|
|||||||
import '../../providers/app_provider.dart';
|
import '../../providers/app_provider.dart';
|
||||||
import '../../models/data_models.dart';
|
import '../../models/data_models.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 回收站页面
|
/// 回收站页面
|
||||||
class RecycleBinPage extends StatefulWidget {
|
class RecycleBinPage extends StatefulWidget {
|
||||||
@@ -441,7 +442,7 @@ class _RecycleBinPageState extends State<RecycleBinPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _restore(_DeletedItem item) async {
|
Future<void> _restore(_DeletedItem item) async {
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
@@ -548,7 +549,7 @@ class _RecycleBinPageState extends State<RecycleBinPage> {
|
|||||||
|
|
||||||
Future<bool> _showConfirmDialog(String message) async {
|
Future<bool> _showConfirmDialog(String message) async {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final result = await showDialog<bool>(
|
final result = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -582,7 +583,7 @@ class _RecycleBinPageState extends State<RecycleBinPage> {
|
|||||||
void _showClearAllDialog() {
|
void _showClearAllDialog() {
|
||||||
final pageContext = context;
|
final pageContext = context;
|
||||||
final colors = Theme.of(pageContext).colorScheme;
|
final colors = Theme.of(pageContext).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: pageContext,
|
context: pageContext,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../../providers/app_provider.dart';
|
import '../../providers/app_provider.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
class TagManagementPage extends StatefulWidget {
|
class TagManagementPage extends StatefulWidget {
|
||||||
const TagManagementPage({super.key});
|
const TagManagementPage({super.key});
|
||||||
@@ -561,7 +562,7 @@ class _TagManagementPageState extends State<TagManagementPage> {
|
|||||||
final name = tag['name'] as String;
|
final name = tag['name'] as String;
|
||||||
final isHidden = (tag['is_hidden'] as int?) == 1;
|
final isHidden = (tag['is_hidden'] as int?) == 1;
|
||||||
|
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
|
||||||
@@ -680,7 +681,7 @@ class _TagManagementPageState extends State<TagManagementPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
@@ -762,7 +763,7 @@ class _TagManagementPageState extends State<TagManagementPage> {
|
|||||||
.where((t) => (t['id'] as String) != tagId && !descendants.contains(t['id'] as String))
|
.where((t) => (t['id'] as String) != tagId && !descendants.contains(t['id'] as String))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
|
||||||
@@ -857,7 +858,7 @@ class _TagManagementPageState extends State<TagManagementPage> {
|
|||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
final type = _currentType;
|
final type = _currentType;
|
||||||
|
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -927,7 +928,7 @@ class _TagManagementPageState extends State<TagManagementPage> {
|
|||||||
final type = tag['type'] as String;
|
final type = tag['type'] as String;
|
||||||
final oldName = tag['name'] as String;
|
final oldName = tag['name'] as String;
|
||||||
|
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
@@ -995,7 +996,7 @@ class _TagManagementPageState extends State<TagManagementPage> {
|
|||||||
.map((t) => t['name'] as String)
|
.map((t) => t['name'] as String)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => StatefulBuilder(
|
builder: (ctx) => StatefulBuilder(
|
||||||
builder: (ctx, setDialogState) {
|
builder: (ctx, setDialogState) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import '../../providers/app_provider.dart';
|
|||||||
import '../../services/sync/backup_service.dart';
|
import '../../services/sync/backup_service.dart';
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../utils/user_prefs.dart';
|
import '../../utils/user_prefs.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 本地备份页面
|
/// 本地备份页面
|
||||||
class BackupPage extends StatefulWidget {
|
class BackupPage extends StatefulWidget {
|
||||||
@@ -500,7 +501,7 @@ class _BackupPageState extends State<BackupPage> {
|
|||||||
required String content,
|
required String content,
|
||||||
String? detail,
|
String? detail,
|
||||||
}) {
|
}) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -600,7 +601,7 @@ class _BackupPageState extends State<BackupPage> {
|
|||||||
/// 导入数据
|
/// 导入数据
|
||||||
Future<void> _importData() async {
|
Future<void> _importData() async {
|
||||||
// 显示确认对话框
|
// 显示确认对话框
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
|
|||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../services/sync/webdav_service.dart';
|
import '../../services/sync/webdav_service.dart';
|
||||||
import '../../providers/app_provider.dart';
|
import '../../providers/app_provider.dart';
|
||||||
|
import '../../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// WebDAV 备份页面
|
/// WebDAV 备份页面
|
||||||
class WebDAVSyncPage extends StatefulWidget {
|
class WebDAVSyncPage extends StatefulWidget {
|
||||||
@@ -183,7 +184,7 @@ class _WebDAVSyncPageState extends State<WebDAVSyncPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showResultDialog(String title, String content) {
|
void _showResultDialog(String title, String content) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -231,7 +232,7 @@ class _WebDAVSyncPageState extends State<WebDAVSyncPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showUploadConfirm() async {
|
Future<void> _showUploadConfirm() async {
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -269,7 +270,7 @@ class _WebDAVSyncPageState extends State<WebDAVSyncPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showDownloadConfirm() async {
|
Future<void> _showDownloadConfirm() async {
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
@@ -307,7 +308,7 @@ class _WebDAVSyncPageState extends State<WebDAVSyncPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _clearConfig() async {
|
Future<void> _clearConfig() async {
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await appDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
import '../models/data_models.dart';
|
import '../models/data_models.dart';
|
||||||
@@ -81,6 +82,10 @@ class AppProvider extends ChangeNotifier {
|
|||||||
// 主题模式
|
// 主题模式
|
||||||
ThemeMode _themeMode = ThemeMode.system;
|
ThemeMode _themeMode = ThemeMode.system;
|
||||||
|
|
||||||
|
// 毛玻璃主题(themeMode=3):标志 + 随机封面
|
||||||
|
bool _frostedActive = false;
|
||||||
|
String? _frostedCoverPath;
|
||||||
|
|
||||||
// 配色方案索引
|
// 配色方案索引
|
||||||
int _colorSchemeIndex = 0;
|
int _colorSchemeIndex = 0;
|
||||||
|
|
||||||
@@ -250,12 +255,14 @@ class AppProvider extends ChangeNotifier {
|
|||||||
// 加载影视数据
|
// 加载影视数据
|
||||||
Future<void> loadMovies() async {
|
Future<void> loadMovies() async {
|
||||||
_movies = await _movieDao.getAllMovies(sortMode: UserPrefs().movieSortMode);
|
_movies = await _movieDao.getAllMovies(sortMode: UserPrefs().movieSortMode);
|
||||||
|
if (_frostedActive && _frostedCoverPath == null) refreshFrostedCover();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载书籍数据
|
// 加载书籍数据
|
||||||
Future<void> loadBooks() async {
|
Future<void> loadBooks() async {
|
||||||
_books = await _bookDao.getAllBooks(sortMode: UserPrefs().bookSortMode);
|
_books = await _bookDao.getAllBooks(sortMode: UserPrefs().bookSortMode);
|
||||||
|
if (_frostedActive && _frostedCoverPath == null) refreshFrostedCover();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,6 +275,7 @@ class AppProvider extends ChangeNotifier {
|
|||||||
// 加载游戏数据
|
// 加载游戏数据
|
||||||
Future<void> loadGames() async {
|
Future<void> loadGames() async {
|
||||||
_games = await _gameDao.getAllGames(sortMode: UserPrefs().gameSortMode);
|
_games = await _gameDao.getAllGames(sortMode: UserPrefs().gameSortMode);
|
||||||
|
if (_frostedActive && _frostedCoverPath == null) refreshFrostedCover();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,6 +344,10 @@ class AppProvider extends ChangeNotifier {
|
|||||||
bool get bottomNavVisible => _bottomNavVisible;
|
bool get bottomNavVisible => _bottomNavVisible;
|
||||||
ThemeMode get themeMode => _themeMode;
|
ThemeMode get themeMode => _themeMode;
|
||||||
int get colorSchemeIndex => _colorSchemeIndex;
|
int get colorSchemeIndex => _colorSchemeIndex;
|
||||||
|
|
||||||
|
// 毛玻璃状态
|
||||||
|
bool get frostedActive => _frostedActive;
|
||||||
|
String? get frostedCoverPath => _frostedCoverPath;
|
||||||
String get fontFamily => _fontFamily;
|
String get fontFamily => _fontFamily;
|
||||||
List<Movie> get movies => UnmodifiableListView(_movies);
|
List<Movie> get movies => UnmodifiableListView(_movies);
|
||||||
List<Book> get books => UnmodifiableListView(_books);
|
List<Book> get books => UnmodifiableListView(_books);
|
||||||
@@ -443,13 +455,35 @@ class AppProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setThemeMode(ThemeMode mode) {
|
void setThemeMode(ThemeMode mode) {
|
||||||
if (_themeMode != mode) {
|
final wasFrosted = _frostedActive;
|
||||||
|
_frostedActive = false;
|
||||||
|
UserPrefs().setThemeMode(mode.index); // 0=system, 1=light, 2=dark, 3=frosted
|
||||||
|
if (_themeMode != mode || wasFrosted) {
|
||||||
_themeMode = mode;
|
_themeMode = mode;
|
||||||
UserPrefs().setThemeMode(mode.index); // 0=system, 1=light, 2=dark
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 启用毛玻璃主题(themeMode=3),随机取一张封面作为背景
|
||||||
|
void enableFrosted() {
|
||||||
|
_frostedActive = true;
|
||||||
|
_themeMode = ThemeMode.dark;
|
||||||
|
UserPrefs().setThemeMode(3);
|
||||||
|
refreshFrostedCover();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 从影视/书籍/游戏封面中随机取一张作为毛玻璃背景
|
||||||
|
void refreshFrostedCover() {
|
||||||
|
if (!_frostedActive) return;
|
||||||
|
final paths = <String>[
|
||||||
|
..._movies.where((m) => m.posterPath != null && m.posterPath!.isNotEmpty).map((m) => m.posterPath!),
|
||||||
|
..._books.where((b) => b.coverPath != null && b.coverPath!.isNotEmpty).map((b) => b.coverPath!),
|
||||||
|
..._games.where((g) => g.coverPath != null && g.coverPath!.isNotEmpty).map((g) => g.coverPath!),
|
||||||
|
];
|
||||||
|
_frostedCoverPath = paths.isEmpty ? null : paths[Random().nextInt(paths.length)];
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
void loadThemeMode() {
|
void loadThemeMode() {
|
||||||
final prefs = UserPrefs();
|
final prefs = UserPrefs();
|
||||||
switch (prefs.themeMode) {
|
switch (prefs.themeMode) {
|
||||||
@@ -457,10 +491,13 @@ class AppProvider extends ChangeNotifier {
|
|||||||
_themeMode = ThemeMode.light;
|
_themeMode = ThemeMode.light;
|
||||||
case 2:
|
case 2:
|
||||||
_themeMode = ThemeMode.dark;
|
_themeMode = ThemeMode.dark;
|
||||||
|
case 3:
|
||||||
|
_frostedActive = true;
|
||||||
|
_themeMode = ThemeMode.dark;
|
||||||
default:
|
default:
|
||||||
_themeMode = ThemeMode.system;
|
_themeMode = ThemeMode.system;
|
||||||
}
|
}
|
||||||
_colorSchemeIndex = prefs.colorSchemeIndex;
|
_colorSchemeIndex = prefs.colorSchemeIndex.clamp(-1, AppTheme.seedColors.length - 1);
|
||||||
_fontFamily = prefs.fontFamily;
|
_fontFamily = prefs.fontFamily;
|
||||||
AppTheme.setFontFamily(_fontFamily);
|
AppTheme.setFontFamily(_fontFamily);
|
||||||
// 异步预加载已缓存的字体(不阻塞 UI)
|
// 异步预加载已缓存的字体(不阻塞 UI)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 图片保存工具 —— 将图片复制/写入到 /sdcard/Pictures/mooknote/
|
/// 图片保存工具 —— 将图片复制/写入到 /sdcard/Pictures/mooknote/
|
||||||
class ImageSaver {
|
class ImageSaver {
|
||||||
@@ -82,7 +83,7 @@ class ImageSaver {
|
|||||||
required Future<void> Function() onConfirm,
|
required Future<void> Function() onConfirm,
|
||||||
}) {
|
}) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
|
|||||||
@@ -181,6 +181,139 @@ class AppTheme {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 毛玻璃主题 - 半透明面板透出背景的暗色玻璃
|
||||||
|
static ThemeData get frostedTheme {
|
||||||
|
const bg = Color(0xFF121418); // 深冷玻璃底(兜底)
|
||||||
|
const surface = Color(0x52121418); // 内容区 32%
|
||||||
|
const surfaceHigh = Color(0x8C121418); // 面板 55%
|
||||||
|
const surfaceHighest = Color(0xA6121418); // 卡片/输入 65%
|
||||||
|
const border = Color(0x1FFFFFFF); // 白 12%
|
||||||
|
const borderStrong = Color(0x2EFFFFFF); // 白 18%
|
||||||
|
const onSurface = Colors.white;
|
||||||
|
const onSurfaceWeak = Color(0x99FFFFFF); // 白 60%
|
||||||
|
const onSurfaceFaint = Color(0x66FFFFFF); // 白 40%
|
||||||
|
const accent = Color(0xFF8AB4FF); // 柔和玻璃蓝
|
||||||
|
|
||||||
|
return ThemeData(
|
||||||
|
useMaterial3: true,
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
scaffoldBackgroundColor: Colors.transparent,
|
||||||
|
colorScheme: const ColorScheme.dark(
|
||||||
|
primary: accent,
|
||||||
|
onPrimary: Color(0xFF0B0D11),
|
||||||
|
secondary: Color(0xFF9AA5B5),
|
||||||
|
onSecondary: bg,
|
||||||
|
surface: surface,
|
||||||
|
onSurface: onSurface,
|
||||||
|
error: Color(0xFFEF6A6A),
|
||||||
|
onError: Color(0xFF2A0A0A),
|
||||||
|
surfaceContainerHigh: surfaceHigh,
|
||||||
|
surfaceContainerHighest: surfaceHighest,
|
||||||
|
outline: borderStrong,
|
||||||
|
outlineVariant: border,
|
||||||
|
),
|
||||||
|
appBarTheme: AppBarTheme(
|
||||||
|
backgroundColor: surfaceHigh,
|
||||||
|
foregroundColor: onSurface,
|
||||||
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
|
surfaceTintColor: Colors.transparent,
|
||||||
|
centerTitle: false,
|
||||||
|
titleSpacing: 24,
|
||||||
|
titleTextStyle: TextStyle(
|
||||||
|
fontFamily: _fontFamily, fontSize: 18, fontWeight: _semibold,
|
||||||
|
color: onSurface, letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
cardTheme: CardThemeData(
|
||||||
|
color: surfaceHighest, elevation: 0,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
side: const BorderSide(color: borderStrong),
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
dialogTheme: DialogThemeData(
|
||||||
|
backgroundColor: const Color(0x99121418), // 60% 深色,透出外层磨砂
|
||||||
|
surfaceTintColor: Colors.transparent,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
side: const BorderSide(color: borderStrong),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bottomSheetTheme: BottomSheetThemeData(
|
||||||
|
backgroundColor: const Color(0xCC121418), // 80% 深色(封装 sheet 用 transparent + FrostedPanel)
|
||||||
|
surfaceTintColor: Colors.transparent,
|
||||||
|
showDragHandle: true,
|
||||||
|
dragHandleColor: borderStrong,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||||
|
side: const BorderSide(color: borderStrong),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
listTileTheme: const ListTileThemeData(
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
|
||||||
|
minLeadingWidth: 0, dense: true,
|
||||||
|
),
|
||||||
|
dividerTheme: DividerThemeData(color: border, thickness: 0.5, space: 0),
|
||||||
|
scrollbarTheme: ScrollbarThemeData(
|
||||||
|
thumbColor: WidgetStateProperty.resolveWith((states) {
|
||||||
|
if (states.contains(WidgetState.hovered)) return onSurface.withValues(alpha: 0.3);
|
||||||
|
return onSurface.withValues(alpha: 0.15);
|
||||||
|
}),
|
||||||
|
thickness: WidgetStateProperty.resolveWith((states) {
|
||||||
|
if (states.contains(WidgetState.hovered)) return 6.0;
|
||||||
|
return 4.0;
|
||||||
|
}),
|
||||||
|
radius: const Radius.circular(3),
|
||||||
|
),
|
||||||
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
filled: false,
|
||||||
|
border: UnderlineInputBorder(borderSide: const BorderSide(color: borderStrong, width: 0.5)),
|
||||||
|
enabledBorder: UnderlineInputBorder(borderSide: const BorderSide(color: borderStrong, width: 0.5)),
|
||||||
|
focusedBorder: UnderlineInputBorder(borderSide: const BorderSide(color: accent, width: 1)),
|
||||||
|
errorBorder: UnderlineInputBorder(borderSide: const BorderSide(color: Color(0xFFEF6A6A), width: 0.5)),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
|
hintStyle: TextStyle(fontFamily: _fontFamily, fontSize: 15, fontWeight: _regular, color: onSurfaceFaint),
|
||||||
|
labelStyle: TextStyle(fontFamily: _fontFamily, fontSize: 13, fontWeight: _medium, color: onSurfaceWeak),
|
||||||
|
),
|
||||||
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: accent, foregroundColor: const Color(0xFF0B0D11),
|
||||||
|
elevation: 0, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
|
textStyle: TextStyle(fontFamily: _fontFamily, fontSize: 14, fontWeight: _medium, letterSpacing: 0.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
textButtonTheme: TextButtonThemeData(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: accent,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
textStyle: TextStyle(fontFamily: _fontFamily, fontSize: 14, fontWeight: _medium),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||||
|
backgroundColor: const Color(0xCC14161A),
|
||||||
|
selectedItemColor: accent,
|
||||||
|
unselectedItemColor: onSurfaceFaint,
|
||||||
|
elevation: 0, type: BottomNavigationBarType.fixed,
|
||||||
|
selectedLabelStyle: TextStyle(fontFamily: _fontFamily, fontSize: 11, fontWeight: _medium),
|
||||||
|
unselectedLabelStyle: TextStyle(fontFamily: _fontFamily, fontSize: 11, fontWeight: _regular, color: onSurfaceFaint),
|
||||||
|
),
|
||||||
|
textTheme: TextTheme(
|
||||||
|
headlineLarge: TextStyle(fontFamily: _fontFamily, fontSize: 32, fontWeight: _semibold, color: onSurface, letterSpacing: 0, height: 1.2),
|
||||||
|
headlineMedium: TextStyle(fontFamily: _fontFamily, fontSize: 24, fontWeight: _semibold, color: onSurface, letterSpacing: 0, height: 1.3),
|
||||||
|
headlineSmall: TextStyle(fontFamily: _fontFamily, fontSize: 20, fontWeight: _semibold, color: onSurface, letterSpacing: 0, height: 1.4),
|
||||||
|
bodyLarge: TextStyle(fontFamily: _fontFamily, fontSize: 16, fontWeight: _regular, color: onSurfaceWeak, height: 1.6),
|
||||||
|
bodyMedium: TextStyle(fontFamily: _fontFamily, fontSize: 15, fontWeight: _regular, color: onSurfaceWeak, height: 1.5),
|
||||||
|
bodySmall: TextStyle(fontFamily: _fontFamily, fontSize: 13, fontWeight: _regular, color: onSurfaceFaint, height: 1.5),
|
||||||
|
labelLarge: TextStyle(fontFamily: _fontFamily, fontSize: 14, fontWeight: _medium, color: onSurface),
|
||||||
|
labelMedium: TextStyle(fontFamily: _fontFamily, fontSize: 12, fontWeight: _medium, color: onSurfaceWeak),
|
||||||
|
labelSmall: TextStyle(fontFamily: _fontFamily, fontSize: 11, fontWeight: _medium, color: onSurfaceFaint, letterSpacing: 0.3),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 亮色主题 - 极简主义
|
// 亮色主题 - 极简主义
|
||||||
static ThemeData get lightTheme {
|
static ThemeData get lightTheme {
|
||||||
return ThemeData(
|
return ThemeData(
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class UserPrefs {
|
|||||||
|
|
||||||
// ========== 应用设置 ==========
|
// ========== 应用设置 ==========
|
||||||
|
|
||||||
/// 主题模式: 0=跟随系统, 1=浅色, 2=深色
|
/// 主题模式: 0=跟随系统, 1=浅色, 2=深色, 3=毛玻璃
|
||||||
int get themeMode => prefs.getInt('themeMode') ?? 0;
|
int get themeMode => prefs.getInt('themeMode') ?? 0;
|
||||||
Future<bool> setThemeMode(int value) => prefs.setInt('themeMode', value);
|
Future<bool> setThemeMode(int value) => prefs.setInt('themeMode', value);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'dart:io' show Platform;
|
import 'dart:io' show Platform;
|
||||||
import '../providers/app_provider.dart';
|
import '../providers/app_provider.dart';
|
||||||
import '../utils/user_prefs.dart';
|
import '../utils/user_prefs.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 新增记录弹窗 — 供底部导航栏和 NavigationRail 共用
|
/// 新增记录弹窗 — 供底部导航栏和 NavigationRail 共用
|
||||||
|
|
||||||
@@ -98,7 +99,7 @@ void showAddSheet(BuildContext context, AppProvider provider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Platform.isWindows) {
|
if (Platform.isWindows) {
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final bc = Theme.of(ctx).colorScheme;
|
final bc = Theme.of(ctx).colorScheme;
|
||||||
@@ -121,7 +122,7 @@ void showAddSheet(BuildContext context, AppProvider provider) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../utils/user_prefs.dart';
|
import '../utils/user_prefs.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 添加类型选择弹窗
|
/// 添加类型选择弹窗
|
||||||
Future<void> showAddTypeDialog(BuildContext context) async {
|
Future<void> showAddTypeDialog(BuildContext context) async {
|
||||||
final result = await showDialog<int>(
|
final result = await appDialog<int>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => const _AddTypeDialog(),
|
builder: (ctx) => const _AddTypeDialog(),
|
||||||
);
|
);
|
||||||
@@ -15,7 +16,7 @@ Future<void> showAddTypeDialog(BuildContext context) async {
|
|||||||
|
|
||||||
/// 返回选中的类型索引 (0=影视, 1=阅读, 2=笔记, 3=游戏),取消返回 null
|
/// 返回选中的类型索引 (0=影视, 1=阅读, 2=笔记, 3=游戏),取消返回 null
|
||||||
Future<int?> showAddTypeSelector(BuildContext context) {
|
Future<int?> showAddTypeSelector(BuildContext context) {
|
||||||
return showDialog<int>(
|
return appDialog<int>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => const _AddTypeDialog(),
|
builder: (ctx) => const _AddTypeDialog(),
|
||||||
);
|
);
|
||||||
|
|||||||
102
lib/widgets/app_overlay.dart
Normal file
102
lib/widgets/app_overlay.dart
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import 'dart:ui' as ui;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import '../providers/app_provider.dart';
|
||||||
|
|
||||||
|
/// 毛玻璃主题下的通用磨砂面板:真实背景模糊 + 半透明深色
|
||||||
|
/// 仅在毛玻璃模式下生效,其余主题直接透传 child,不影响原样式
|
||||||
|
class FrostedPanel extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
final BorderRadiusGeometry borderRadius;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
const FrostedPanel({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
this.borderRadius = BorderRadius.zero,
|
||||||
|
this.color = const Color(0xCC121418),
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final frosted = context.watch<AppProvider>().frostedActive;
|
||||||
|
if (!frosted) return child;
|
||||||
|
return ClipRRect(
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
child: BackdropFilter(
|
||||||
|
filter: ui.ImageFilter.blur(sigmaX: 22, sigmaY: 22),
|
||||||
|
child: ColoredBox(color: color, child: child),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 毛玻璃版底部弹层:毛玻璃模式下自动加磨砂,其余主题与原 showModalBottomSheet 一致
|
||||||
|
Future<T?> appModalBottomSheet<T>({
|
||||||
|
required BuildContext context,
|
||||||
|
Color? backgroundColor,
|
||||||
|
Color? barrierColor,
|
||||||
|
double? elevation,
|
||||||
|
ShapeBorder? shape,
|
||||||
|
bool isScrollControlled = false,
|
||||||
|
bool isDismissible = true,
|
||||||
|
bool enableDrag = true,
|
||||||
|
bool useSafeArea = false,
|
||||||
|
bool showDragHandle = false,
|
||||||
|
BoxConstraints? constraints,
|
||||||
|
bool useRootNavigator = false,
|
||||||
|
required WidgetBuilder builder,
|
||||||
|
}) {
|
||||||
|
final frosted = context.read<AppProvider>().frostedActive;
|
||||||
|
return showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
backgroundColor: frosted ? Colors.transparent : backgroundColor,
|
||||||
|
barrierColor: barrierColor,
|
||||||
|
elevation: elevation,
|
||||||
|
shape: shape,
|
||||||
|
isScrollControlled: isScrollControlled,
|
||||||
|
isDismissible: isDismissible,
|
||||||
|
enableDrag: enableDrag,
|
||||||
|
useSafeArea: useSafeArea,
|
||||||
|
showDragHandle: showDragHandle,
|
||||||
|
constraints: constraints,
|
||||||
|
useRootNavigator: useRootNavigator,
|
||||||
|
builder: (ctx) {
|
||||||
|
final content = builder(ctx);
|
||||||
|
if (!frosted) return content;
|
||||||
|
return FrostedPanel(
|
||||||
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
|
||||||
|
child: content,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 毛玻璃版对话框:毛玻璃模式下自动加磨砂
|
||||||
|
Future<T?> appDialog<T>({
|
||||||
|
required BuildContext context,
|
||||||
|
bool barrierDismissible = true,
|
||||||
|
Color? barrierColor,
|
||||||
|
bool useSafeArea = true,
|
||||||
|
RouteSettings? routeSettings,
|
||||||
|
bool useRootNavigator = true,
|
||||||
|
required WidgetBuilder builder,
|
||||||
|
}) {
|
||||||
|
final frosted = context.read<AppProvider>().frostedActive;
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: barrierDismissible,
|
||||||
|
barrierColor: barrierColor,
|
||||||
|
useSafeArea: useSafeArea,
|
||||||
|
routeSettings: routeSettings,
|
||||||
|
useRootNavigator: useRootNavigator,
|
||||||
|
builder: (ctx) {
|
||||||
|
final content = builder(ctx);
|
||||||
|
if (!frosted) return content;
|
||||||
|
return FrostedPanel(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
child: content,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import '../providers/app_provider.dart';
|
|||||||
import '../widgets/fade_in_local_image.dart';
|
import '../widgets/fade_in_local_image.dart';
|
||||||
import '../widgets/animated_star_rating.dart';
|
import '../widgets/animated_star_rating.dart';
|
||||||
import '../utils/toast_util.dart';
|
import '../utils/toast_util.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 书籍列表项组件 - 网格布局设计
|
/// 书籍列表项组件 - 网格布局设计
|
||||||
class BookListItem extends StatelessWidget {
|
class BookListItem extends StatelessWidget {
|
||||||
@@ -73,7 +74,7 @@ class BookListItem extends StatelessWidget {
|
|||||||
|
|
||||||
void _showDeleteDialog(BuildContext context) {
|
void _showDeleteDialog(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:ui' as ui;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../providers/app_provider.dart';
|
import '../providers/app_provider.dart';
|
||||||
@@ -10,9 +11,6 @@ class CustomBottomNavBar extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final bottomPadding = MediaQuery.of(context).padding.bottom;
|
final bottomPadding = MediaQuery.of(context).padding.bottom;
|
||||||
final colors = Theme.of(context).colorScheme;
|
|
||||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
|
||||||
|
|
||||||
return Consumer<AppProvider>(
|
return Consumer<AppProvider>(
|
||||||
builder: (context, provider, child) {
|
builder: (context, provider, child) {
|
||||||
return Container(
|
return Container(
|
||||||
@@ -21,9 +19,23 @@ class CustomBottomNavBar extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
_buildDock(context, provider: provider),
|
||||||
|
SizedBox(height: bottomPadding + 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dock 悬浮导航条;毛玻璃模式下加真实模糊,保证可读
|
||||||
|
Widget _buildDock(BuildContext context, {required AppProvider provider}) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
|
final isFrosted = provider.frostedActive;
|
||||||
|
|
||||||
|
final dock = Container(
|
||||||
height: 56,
|
height: 56,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 40),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colors.surface,
|
color: colors.surface,
|
||||||
borderRadius: BorderRadius.circular(28),
|
borderRadius: BorderRadius.circular(28),
|
||||||
@@ -62,12 +74,22 @@ class CustomBottomNavBar extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
SizedBox(height: bottomPadding + 8),
|
|
||||||
],
|
// 毛玻璃模式下:裁剪 + 模糊区域与胶囊同宽(外围留白),避免整行全宽模糊
|
||||||
|
Widget result = dock;
|
||||||
|
if (isFrosted) {
|
||||||
|
result = ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(28),
|
||||||
|
child: BackdropFilter(
|
||||||
|
filter: ui.ImageFilter.blur(sigmaX: 25, sigmaY: 25),
|
||||||
|
child: dock,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||||
|
child: result,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../pages/character/character_form_page.dart';
|
import '../pages/character/character_form_page.dart';
|
||||||
import 'fade_in_local_image.dart';
|
import 'fade_in_local_image.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 角色信息底部弹窗
|
/// 角色信息底部弹窗
|
||||||
///
|
///
|
||||||
@@ -26,7 +27,7 @@ class CharacterInfoSheet extends StatefulWidget {
|
|||||||
required String entityId,
|
required String entityId,
|
||||||
required dynamic character,
|
required dynamic character,
|
||||||
}) {
|
}) {
|
||||||
return showModalBottomSheet<bool>(
|
return appModalBottomSheet<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
@@ -27,6 +28,7 @@ import '../pages/profile/settings_page.dart';
|
|||||||
import '../models/data_models.dart';
|
import '../models/data_models.dart';
|
||||||
import 'fade_in_local_image.dart';
|
import 'fade_in_local_image.dart';
|
||||||
import 'shimmer_skeleton.dart';
|
import 'shimmer_skeleton.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 自定义侧边栏
|
/// 自定义侧边栏
|
||||||
class CustomDrawer extends StatefulWidget {
|
class CustomDrawer extends StatefulWidget {
|
||||||
@@ -194,6 +196,18 @@ class _CustomDrawerState extends State<CustomDrawer> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (widget.embedded) return content;
|
if (widget.embedded) return content;
|
||||||
|
final isFrosted = context.read<AppProvider>().frostedActive;
|
||||||
|
if (isFrosted) {
|
||||||
|
return Drawer(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
child: ClipRect(
|
||||||
|
child: BackdropFilter(
|
||||||
|
filter: ui.ImageFilter.blur(sigmaX: 25, sigmaY: 25),
|
||||||
|
child: Container(color: colors.surfaceContainerHigh, child: content),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
return Drawer(
|
return Drawer(
|
||||||
backgroundColor: colors.surfaceContainerHigh,
|
backgroundColor: colors.surfaceContainerHigh,
|
||||||
child: content,
|
child: content,
|
||||||
@@ -689,7 +703,7 @@ class _CustomDrawerState extends State<CustomDrawer> {
|
|||||||
final dayNotes = notes.where((n) => !n.isDeleted && DateTime(n.createdAt.year, n.createdAt.month, n.createdAt.day) == date).toList();
|
final dayNotes = notes.where((n) => !n.isDeleted && DateTime(n.createdAt.year, n.createdAt.month, n.createdAt.day) == date).toList();
|
||||||
final dayGames = games.where((g) => !g.isDeleted && DateTime(g.createdAt.year, g.createdAt.month, g.createdAt.day) == date).toList();
|
final dayGames = games.where((g) => !g.isDeleted && DateTime(g.createdAt.year, g.createdAt.month, g.createdAt.day) == date).toList();
|
||||||
|
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 时长选择器(时:分两个滚轮的底部弹窗)
|
/// 时长选择器(时:分两个滚轮的底部弹窗)
|
||||||
/// 返回总分钟数,取消返回 null
|
/// 返回总分钟数,取消返回 null
|
||||||
@@ -11,7 +12,7 @@ class DurationPicker {
|
|||||||
int initialMinutes = 0,
|
int initialMinutes = 0,
|
||||||
String title = '影视总时长',
|
String title = '影视总时长',
|
||||||
}) {
|
}) {
|
||||||
return showModalBottomSheet<int>(
|
return appModalBottomSheet<int>(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
|
|||||||
35
lib/widgets/frosted_background.dart
Normal file
35
lib/widgets/frosted_background.dart
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// 毛玻璃主题背景层:封面图 + 模糊 + 深色遮罩
|
||||||
|
/// 用 ImageFiltered 模糊静态图片(开销低于 BackdropFilter 实时模糊)
|
||||||
|
class FrostedBackground extends StatelessWidget {
|
||||||
|
final String? coverPath;
|
||||||
|
|
||||||
|
const FrostedBackground({super.key, this.coverPath});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (coverPath == null || coverPath!.isEmpty) {
|
||||||
|
// 无封面兜底:纯色玻璃底
|
||||||
|
return const ColoredBox(color: Color(0xFF121418));
|
||||||
|
}
|
||||||
|
return Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
ImageFiltered(
|
||||||
|
imageFilter: ui.ImageFilter.blur(sigmaX: 25, sigmaY: 25),
|
||||||
|
child: Image.file(
|
||||||
|
File(coverPath!),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
errorBuilder: (_, __, ___) =>
|
||||||
|
const ColoredBox(color: Color(0xFF121418)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 深色遮罩,保证内容可读
|
||||||
|
const ColoredBox(color: Color(0x73000000)), // 黑 ~45%
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import '../providers/app_provider.dart';
|
|||||||
import '../widgets/fade_in_local_image.dart';
|
import '../widgets/fade_in_local_image.dart';
|
||||||
import '../widgets/animated_star_rating.dart';
|
import '../widgets/animated_star_rating.dart';
|
||||||
import '../utils/toast_util.dart';
|
import '../utils/toast_util.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 游戏列表项组件 - 网格布局设计
|
/// 游戏列表项组件 - 网格布局设计
|
||||||
class GameListItem extends StatelessWidget {
|
class GameListItem extends StatelessWidget {
|
||||||
@@ -73,7 +74,7 @@ class GameListItem extends StatelessWidget {
|
|||||||
|
|
||||||
void _showDeleteDialog(BuildContext context) {
|
void _showDeleteDialog(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 类型/标签选择右侧弹窗(影视类型、导演、编剧、主演、书籍类型通用)
|
/// 类型/标签选择右侧弹窗(影视类型、导演、编剧、主演、书籍类型通用)
|
||||||
class GenreSelectorPage extends StatefulWidget {
|
class GenreSelectorPage extends StatefulWidget {
|
||||||
@@ -109,7 +110,7 @@ class _GenreSelectorPageState extends State<GenreSelectorPage> {
|
|||||||
|
|
||||||
void _editItem(int index, String oldValue) {
|
void _editItem(int index, String oldValue) {
|
||||||
final editController = TextEditingController(text: oldValue);
|
final editController = TextEditingController(text: oldValue);
|
||||||
showDialog<String>(
|
appDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import '../providers/app_provider.dart';
|
|||||||
import '../widgets/fade_in_local_image.dart';
|
import '../widgets/fade_in_local_image.dart';
|
||||||
import '../widgets/animated_star_rating.dart';
|
import '../widgets/animated_star_rating.dart';
|
||||||
import '../utils/toast_util.dart';
|
import '../utils/toast_util.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 观影列表项组件 - 网格布局设计
|
/// 观影列表项组件 - 网格布局设计
|
||||||
class MovieListItem extends StatelessWidget {
|
class MovieListItem extends StatelessWidget {
|
||||||
@@ -73,7 +74,7 @@ class MovieListItem extends StatelessWidget {
|
|||||||
|
|
||||||
void _showDeleteDialog(BuildContext context) {
|
void _showDeleteDialog(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
|
|||||||
import '../providers/app_provider.dart';
|
import '../providers/app_provider.dart';
|
||||||
import '../models/data_models.dart';
|
import '../models/data_models.dart';
|
||||||
import 'fade_in_local_image.dart';
|
import 'fade_in_local_image.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 笔记列表项组件 - 卡片式设计,内容展示在卡片内
|
/// 笔记列表项组件 - 卡片式设计,内容展示在卡片内
|
||||||
class NoteListItem extends StatelessWidget {
|
class NoteListItem extends StatelessWidget {
|
||||||
@@ -189,7 +190,7 @@ class _NoteListItemContent extends StatelessWidget {
|
|||||||
|
|
||||||
void _showActions(BuildContext context) {
|
void _showActions(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
@@ -272,7 +273,7 @@ class _NoteListItemContent extends StatelessWidget {
|
|||||||
|
|
||||||
void _showDeleteConfirm(BuildContext context) {
|
void _showDeleteConfirm(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import '../pages/people/person_detail_page.dart';
|
|||||||
import '../providers/app_provider.dart';
|
import '../providers/app_provider.dart';
|
||||||
import 'fade_in_local_image.dart';
|
import 'fade_in_local_image.dart';
|
||||||
import 'person_avatar.dart';
|
import 'person_avatar.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 人物信息浮动面板(底部 ModalBottomSheet)
|
/// 人物信息浮动面板(底部 ModalBottomSheet)
|
||||||
/// 展示人物基本信息 + 关联作品,点击「查看全部」跳转到原详情页
|
/// 展示人物基本信息 + 关联作品,点击「查看全部」跳转到原详情页
|
||||||
@@ -14,7 +15,7 @@ class PersonInfoSheet extends StatefulWidget {
|
|||||||
const PersonInfoSheet({super.key, required this.person});
|
const PersonInfoSheet({super.key, required this.person});
|
||||||
|
|
||||||
static Future<void> show(BuildContext context, Person person) {
|
static Future<void> show(BuildContext context, Person person) {
|
||||||
return showModalBottomSheet(
|
return appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:uuid/uuid.dart';
|
|||||||
import '../models/data_models.dart';
|
import '../models/data_models.dart';
|
||||||
import '../providers/app_provider.dart';
|
import '../providers/app_provider.dart';
|
||||||
import 'fade_in_local_image.dart';
|
import 'fade_in_local_image.dart';
|
||||||
|
import '../widgets/app_overlay.dart';
|
||||||
|
|
||||||
/// 人物详情页使用的作品关联结果(按媒体类型分组)
|
/// 人物详情页使用的作品关联结果(按媒体类型分组)
|
||||||
class WorkSelectionResult {
|
class WorkSelectionResult {
|
||||||
@@ -382,7 +383,7 @@ class _WorkSelectorPageState extends State<WorkSelectorPage> {
|
|||||||
final title = isVoiceActor ? '配音角色' : '饰演角色';
|
final title = isVoiceActor ? '配音角色' : '饰演角色';
|
||||||
final hint = isVoiceActor ? '如:米老鼠' : '如:关羽';
|
final hint = isVoiceActor ? '如:米老鼠' : '如:关羽';
|
||||||
final ctrl = TextEditingController(text: current ?? '');
|
final ctrl = TextEditingController(text: current ?? '');
|
||||||
showDialog(
|
appDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
final colors = Theme.of(ctx).colorScheme;
|
final colors = Theme.of(ctx).colorScheme;
|
||||||
|
|||||||
Reference in New Issue
Block a user