feat: 主题颜色选择
This commit is contained in:
@ -7,6 +7,7 @@ import 'package:dynamic_color/dynamic_color.dart';
|
|||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/common/widgets/custom_toast.dart';
|
import 'package:pilipala/common/widgets/custom_toast.dart';
|
||||||
import 'package:pilipala/http/init.dart';
|
import 'package:pilipala/http/init.dart';
|
||||||
|
import 'package:pilipala/models/common/color_type.dart';
|
||||||
import 'package:pilipala/models/common/theme_type.dart';
|
import 'package:pilipala/models/common/theme_type.dart';
|
||||||
import 'package:pilipala/pages/search/index.dart';
|
import 'package:pilipala/pages/search/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/index.dart';
|
import 'package:pilipala/pages/video/detail/index.dart';
|
||||||
@ -35,15 +36,24 @@ class MyApp extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color brandColor = const Color.fromARGB(255, 92, 182, 123);
|
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
|
// 主题色
|
||||||
|
Color defaultColor =
|
||||||
|
colorThemeTypes[setting.get(SettingBoxKey.customColor, defaultValue: 0)]
|
||||||
|
['color'];
|
||||||
|
Color brandColor = defaultColor;
|
||||||
|
// 主题模式
|
||||||
ThemeType currentThemeValue = ThemeType.values[setting
|
ThemeType currentThemeValue = ThemeType.values[setting
|
||||||
.get(SettingBoxKey.themeMode, defaultValue: ThemeType.system.code)];
|
.get(SettingBoxKey.themeMode, defaultValue: ThemeType.system.code)];
|
||||||
|
// 是否动态取色
|
||||||
|
bool isDynamicColor =
|
||||||
|
setting.get(SettingBoxKey.dynamicColor, defaultValue: true);
|
||||||
|
|
||||||
return DynamicColorBuilder(
|
return DynamicColorBuilder(
|
||||||
builder: ((ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
builder: ((ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
||||||
ColorScheme? lightColorScheme;
|
ColorScheme? lightColorScheme;
|
||||||
ColorScheme? darkColorScheme;
|
ColorScheme? darkColorScheme;
|
||||||
if (lightDynamic != null && darkDynamic != null) {
|
if (lightDynamic != null && darkDynamic != null && isDynamicColor) {
|
||||||
// dynamic取色成功
|
// dynamic取色成功
|
||||||
lightColorScheme = lightDynamic.harmonized();
|
lightColorScheme = lightDynamic.harmonized();
|
||||||
darkColorScheme = darkDynamic.harmonized();
|
darkColorScheme = darkDynamic.harmonized();
|
||||||
@ -93,9 +103,15 @@ class MyApp extends StatelessWidget {
|
|||||||
fallbackLocale: const Locale("zh", "CN"),
|
fallbackLocale: const Locale("zh", "CN"),
|
||||||
getPages: Routes.getPages,
|
getPages: Routes.getPages,
|
||||||
home: const MainApp(),
|
home: const MainApp(),
|
||||||
builder: FlutterSmartDialog.init(
|
builder: (BuildContext context, Widget? child) {
|
||||||
toastBuilder: (String msg) => CustomToast(msg: msg),
|
return FlutterSmartDialog(
|
||||||
),
|
toastBuilder: (String msg) => CustomToast(msg: msg),
|
||||||
|
child: MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
|
||||||
|
child: child!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
navigatorObservers: [
|
navigatorObservers: [
|
||||||
VideoDetailPage.routeObserver,
|
VideoDetailPage.routeObserver,
|
||||||
SearchPage.routeObserver,
|
SearchPage.routeObserver,
|
||||||
|
|||||||
23
lib/models/common/color_type.dart
Normal file
23
lib/models/common/color_type.dart
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final List<Map<String, dynamic>> colorThemeTypes = [
|
||||||
|
{'color': const Color.fromARGB(255, 92, 182, 123), 'label': '默认绿'},
|
||||||
|
{'color': Colors.pink, 'label': '粉红色'},
|
||||||
|
{'color': Colors.red, 'label': '红色'},
|
||||||
|
{'color': Colors.orange, 'label': '橙色'},
|
||||||
|
{'color': Colors.amber, 'label': '琥珀色'},
|
||||||
|
{'color': Colors.yellow, 'label': '黄色'},
|
||||||
|
{'color': Colors.lime, 'label': '酸橙色'},
|
||||||
|
{'color': Colors.lightGreen, 'label': '浅绿色'},
|
||||||
|
{'color': Colors.green, 'label': '绿色'},
|
||||||
|
{'color': Colors.teal, 'label': '青色'},
|
||||||
|
{'color': Colors.cyan, 'label': '蓝绿色'},
|
||||||
|
{'color': Colors.lightBlue, 'label': '浅蓝色'},
|
||||||
|
{'color': Colors.blue, 'label': '蓝色'},
|
||||||
|
{'color': Colors.indigo, 'label': '靛蓝色'},
|
||||||
|
{'color': Colors.purple, 'label': '紫色'},
|
||||||
|
{'color': Colors.deepPurple, 'label': '深紫色'},
|
||||||
|
{'color': Colors.blueGrey, 'label': '蓝灰色'},
|
||||||
|
{'color': Colors.brown, 'label': '棕色'},
|
||||||
|
{'color': Colors.grey, 'label': '灰色'},
|
||||||
|
];
|
||||||
162
lib/pages/setting/pages/color_select.dart
Normal file
162
lib/pages/setting/pages/color_select.dart
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:pilipala/models/common/color_type.dart';
|
||||||
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
|
class ColorSelectPage extends StatefulWidget {
|
||||||
|
const ColorSelectPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ColorSelectPage> createState() => _ColorSelectPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Item {
|
||||||
|
Item({
|
||||||
|
required this.expandedValue,
|
||||||
|
required this.headerValue,
|
||||||
|
this.isExpanded = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
String expandedValue;
|
||||||
|
String headerValue;
|
||||||
|
bool isExpanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Item> generateItems(int count) {
|
||||||
|
return List<Item>.generate(count, (int index) {
|
||||||
|
return Item(
|
||||||
|
headerValue: 'Panel $index',
|
||||||
|
expandedValue: 'This is item number $index',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ColorSelectPageState extends State<ColorSelectPage> {
|
||||||
|
final ColorSelectController ctr = Get.put(ColorSelectController());
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: false,
|
||||||
|
title: const Text('选择应用主题'),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
Obx(
|
||||||
|
() => RadioListTile(
|
||||||
|
value: 0,
|
||||||
|
title: const Text('动态取色'),
|
||||||
|
groupValue: ctr.type.value,
|
||||||
|
onChanged: (dynamic val) async {
|
||||||
|
ctr.type.value = 0;
|
||||||
|
ctr.setting.put(SettingBoxKey.dynamicColor, true);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() => RadioListTile(
|
||||||
|
value: 1,
|
||||||
|
title: const Text('指定颜色'),
|
||||||
|
groupValue: ctr.type.value,
|
||||||
|
onChanged: (dynamic val) async {
|
||||||
|
ctr.type.value = 1;
|
||||||
|
ctr.setting.put(SettingBoxKey.dynamicColor, false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() {
|
||||||
|
int type = ctr.type.value;
|
||||||
|
return AnimatedOpacity(
|
||||||
|
opacity: type == 1 ? 1 : 0,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 12, left: 12, right: 12),
|
||||||
|
child: Wrap(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
spacing: 22,
|
||||||
|
runSpacing: 18,
|
||||||
|
children: [
|
||||||
|
...ctr.colorThemes.map(
|
||||||
|
(e) {
|
||||||
|
final index = ctr.colorThemes.indexOf(e);
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
ctr.currentColor.value = index;
|
||||||
|
ctr.setting.put(SettingBoxKey.customColor, index);
|
||||||
|
Get.forceAppUpdate();
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 46,
|
||||||
|
height: 46,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: e['color'].withOpacity(0.8),
|
||||||
|
borderRadius: BorderRadius.circular(50),
|
||||||
|
border: Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: ctr.currentColor.value == index
|
||||||
|
? Colors.black
|
||||||
|
: e['color'].withOpacity(0.8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
opacity:
|
||||||
|
ctr.currentColor.value == index ? 1 : 0,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.done,
|
||||||
|
color: Colors.black,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 3),
|
||||||
|
Text(
|
||||||
|
e['label'],
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: ctr.currentColor.value != index
|
||||||
|
? Theme.of(context).colorScheme.outline
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ColorSelectController extends GetxController {
|
||||||
|
Box setting = GStrorage.setting;
|
||||||
|
RxBool dynamicColor = true.obs;
|
||||||
|
RxInt type = 0.obs;
|
||||||
|
late final List<Map<String, dynamic>> colorThemes;
|
||||||
|
RxInt currentColor = 0.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
colorThemes = colorThemeTypes;
|
||||||
|
// 默认使用动态取色
|
||||||
|
dynamicColor.value =
|
||||||
|
setting.get(SettingBoxKey.dynamicColor, defaultValue: true);
|
||||||
|
type.value = dynamicColor.value ? 0 : 1;
|
||||||
|
currentColor.value =
|
||||||
|
setting.get(SettingBoxKey.customColor, defaultValue: 0);
|
||||||
|
super.onInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -184,6 +184,11 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
style: subTitleStyle)),
|
style: subTitleStyle)),
|
||||||
trailing: const Icon(Icons.arrow_right_alt_outlined),
|
trailing: const Icon(Icons.arrow_right_alt_outlined),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
dense: false,
|
||||||
|
onTap: () => Get.toNamed('/colorSetting'),
|
||||||
|
title: Text('应用主题', style: titleStyle),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import 'package:pilipala/pages/preview/index.dart';
|
|||||||
import 'package:pilipala/pages/search/index.dart';
|
import 'package:pilipala/pages/search/index.dart';
|
||||||
import 'package:pilipala/pages/searchResult/index.dart';
|
import 'package:pilipala/pages/searchResult/index.dart';
|
||||||
import 'package:pilipala/pages/setting/extra_setting.dart';
|
import 'package:pilipala/pages/setting/extra_setting.dart';
|
||||||
|
import 'package:pilipala/pages/setting/pages/color_select.dart';
|
||||||
import 'package:pilipala/pages/setting/play_setting.dart';
|
import 'package:pilipala/pages/setting/play_setting.dart';
|
||||||
import 'package:pilipala/pages/setting/privacy_setting.dart';
|
import 'package:pilipala/pages/setting/privacy_setting.dart';
|
||||||
import 'package:pilipala/pages/setting/style_setting.dart';
|
import 'package:pilipala/pages/setting/style_setting.dart';
|
||||||
@ -85,6 +86,7 @@ class Routes {
|
|||||||
GetPage(name: '/extraSetting', page: () => const ExtraSetting()),
|
GetPage(name: '/extraSetting', page: () => const ExtraSetting()),
|
||||||
//
|
//
|
||||||
GetPage(name: '/blackListPage', page: () => const BlackListPage()),
|
GetPage(name: '/blackListPage', page: () => const BlackListPage()),
|
||||||
|
GetPage(name: '/colorSetting', page: () => const ColorSelectPage()),
|
||||||
// 关于
|
// 关于
|
||||||
GetPage(name: '/about', page: () => const AboutPage()),
|
GetPage(name: '/about', page: () => const AboutPage()),
|
||||||
];
|
];
|
||||||
|
|||||||
@ -82,24 +82,27 @@ class GStrorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SettingBoxKey {
|
class SettingBoxKey {
|
||||||
static const String themeMode = 'themeMode';
|
static const String btmProgressBehavior = 'btmProgressBehavior';
|
||||||
static const String feedBackEnable = 'feedBackEnable';
|
|
||||||
static const String defaultFontSize = 'fontSize';
|
|
||||||
static const String defaultVideoQa = 'defaultVideoQa';
|
|
||||||
static const String defaultAudioQa = 'defaultAudioQa';
|
|
||||||
static const String defaultDecode = 'defaultDecode';
|
|
||||||
static const String defaultVideoSpeed = 'defaultVideoSpeed';
|
static const String defaultVideoSpeed = 'defaultVideoSpeed';
|
||||||
static const String autoUpgradeEnable = 'autoUpgradeEnable';
|
static const String autoUpgradeEnable = 'autoUpgradeEnable';
|
||||||
|
static const String feedBackEnable = 'feedBackEnable';
|
||||||
|
static const String defaultVideoQa = 'defaultVideoQa';
|
||||||
|
static const String defaultAudioQa = 'defaultAudioQa';
|
||||||
static const String autoPlayEnable = 'autoPlayEnable';
|
static const String autoPlayEnable = 'autoPlayEnable';
|
||||||
static const String enableHA = 'enableHA';
|
|
||||||
static const String defaultPicQa = 'defaultPicQa';
|
|
||||||
|
|
||||||
static const String danmakuEnable = 'danmakuEnable';
|
|
||||||
static const String fullScreenMode = 'fullScreenMode';
|
static const String fullScreenMode = 'fullScreenMode';
|
||||||
|
static const String defaultDecode = 'defaultDecode';
|
||||||
|
static const String danmakuEnable = 'danmakuEnable';
|
||||||
|
static const String defaultPicQa = 'defaultPicQa';
|
||||||
|
static const String enableHA = 'enableHA';
|
||||||
|
|
||||||
static const String blackMidsList = 'blackMidsList';
|
static const String blackMidsList = 'blackMidsList';
|
||||||
|
|
||||||
static const String autoUpdate = 'autoUpdate';
|
static const String autoUpdate = 'autoUpdate';
|
||||||
static const String btmProgressBehavior = 'btmProgressBehavior';
|
|
||||||
|
static const String themeMode = 'themeMode';
|
||||||
|
static const String defaultFontSize = 'fontSize';
|
||||||
|
static const String dynamicColor = 'dynamicColor'; // bool
|
||||||
|
static const String customColor = 'customColor'; // 自定义主题色
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalCacheKey {
|
class LocalCacheKey {
|
||||||
|
|||||||
Reference in New Issue
Block a user