feat: 首页tabbar 编辑排序
This commit is contained in:
@ -9,6 +9,7 @@ enum TabType { live, rcmd, hot, bangumi }
|
|||||||
|
|
||||||
extension TabTypeDesc on TabType {
|
extension TabTypeDesc on TabType {
|
||||||
String get description => ['直播', '推荐', '热门', '番剧'][index];
|
String get description => ['直播', '推荐', '热门', '番剧'][index];
|
||||||
|
String get id => ['live', 'rcmd', 'hot', 'bangumi'][index];
|
||||||
}
|
}
|
||||||
|
|
||||||
List tabsConfig = [
|
List tabsConfig = [
|
||||||
|
@ -8,12 +8,13 @@ import 'package:pilipala/utils/storage.dart';
|
|||||||
|
|
||||||
class HomeController extends GetxController with GetTickerProviderStateMixin {
|
class HomeController extends GetxController with GetTickerProviderStateMixin {
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
late List tabs;
|
late RxList tabs = [].obs;
|
||||||
RxInt initialIndex = 1.obs;
|
RxInt initialIndex = 1.obs;
|
||||||
late TabController tabController;
|
late TabController tabController;
|
||||||
late List tabsCtrList;
|
late List tabsCtrList;
|
||||||
late List<Widget> tabsPageList;
|
late List<Widget> tabsPageList;
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
|
Box settingStorage = GStrorage.setting;
|
||||||
RxBool userLogin = false.obs;
|
RxBool userLogin = false.obs;
|
||||||
RxString userFace = ''.obs;
|
RxString userFace = ''.obs;
|
||||||
var userInfo;
|
var userInfo;
|
||||||
@ -21,6 +22,8 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
|||||||
late final StreamController<bool> searchBarStream =
|
late final StreamController<bool> searchBarStream =
|
||||||
StreamController<bool>.broadcast();
|
StreamController<bool>.broadcast();
|
||||||
late bool hideSearchBar;
|
late bool hideSearchBar;
|
||||||
|
late List defaultTabs;
|
||||||
|
late List<String> tabbarSort;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -28,34 +31,10 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
|||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
userLogin.value = userInfo != null;
|
userLogin.value = userInfo != null;
|
||||||
userFace.value = userInfo != null ? userInfo.face : '';
|
userFace.value = userInfo != null ? userInfo.face : '';
|
||||||
|
|
||||||
// 进行tabs配置
|
// 进行tabs配置
|
||||||
tabs = tabsConfig;
|
setTabConfig();
|
||||||
tabsCtrList = tabsConfig.map((e) => e['ctr']).toList();
|
|
||||||
tabsPageList = tabsConfig.map<Widget>((e) => e['page']).toList();
|
|
||||||
|
|
||||||
tabController = TabController(
|
|
||||||
initialIndex: initialIndex.value,
|
|
||||||
length: tabs.length,
|
|
||||||
vsync: this,
|
|
||||||
);
|
|
||||||
hideSearchBar =
|
hideSearchBar =
|
||||||
setting.get(SettingBoxKey.hideSearchBar, defaultValue: true);
|
setting.get(SettingBoxKey.hideSearchBar, defaultValue: true);
|
||||||
|
|
||||||
// 监听 tabController 切换
|
|
||||||
tabController.animation!.addListener(() {
|
|
||||||
if (tabController.indexIsChanging) {
|
|
||||||
if (initialIndex.value != tabController.index) {
|
|
||||||
initialIndex.value = tabController.index;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final int temp = tabController.animation!.value.round();
|
|
||||||
if (initialIndex.value != temp) {
|
|
||||||
initialIndex.value = temp;
|
|
||||||
tabController.index = initialIndex.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onRefresh() {
|
void onRefresh() {
|
||||||
@ -77,4 +56,42 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
|||||||
if (val) return;
|
if (val) return;
|
||||||
userFace.value = userInfo != null ? userInfo.face : '';
|
userFace.value = userInfo != null ? userInfo.face : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setTabConfig() async {
|
||||||
|
defaultTabs = tabsConfig;
|
||||||
|
tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort,
|
||||||
|
defaultValue: ['live', 'rcmd', 'hot', 'bangumi']);
|
||||||
|
|
||||||
|
tabs.value = defaultTabs
|
||||||
|
.where((i) => tabbarSort.contains((i['type'] as TabType).id))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (tabbarSort.contains(TabType.rcmd.id)) {
|
||||||
|
initialIndex.value = tabbarSort.indexOf(TabType.rcmd.id);
|
||||||
|
} else {
|
||||||
|
initialIndex.value = 0;
|
||||||
|
}
|
||||||
|
tabsCtrList = tabs.map((e) => e['ctr']).toList();
|
||||||
|
tabsPageList = tabs.map<Widget>((e) => e['page']).toList();
|
||||||
|
|
||||||
|
tabController = TabController(
|
||||||
|
initialIndex: initialIndex.value,
|
||||||
|
length: tabs.length,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
// 监听 tabController 切换
|
||||||
|
tabController.animation!.addListener(() {
|
||||||
|
if (tabController.indexIsChanging) {
|
||||||
|
if (initialIndex.value != tabController.index) {
|
||||||
|
initialIndex.value = tabController.index;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final int temp = tabController.animation!.value.round();
|
||||||
|
if (initialIndex.value != temp) {
|
||||||
|
initialIndex.value = temp;
|
||||||
|
tabController.index = initialIndex.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,11 @@ class _HomePageState extends State<HomePage>
|
|||||||
ctr: _homeController,
|
ctr: _homeController,
|
||||||
callback: showUserBottomSheet,
|
callback: showUserBottomSheet,
|
||||||
),
|
),
|
||||||
const CustomTabs(),
|
if (_homeController.tabs.length > 1) ...[
|
||||||
|
const CustomTabs(),
|
||||||
|
] else ...[
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
],
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
controller: _homeController.tabController,
|
controller: _homeController.tabController,
|
||||||
@ -250,17 +254,6 @@ class CustomTabs extends StatefulWidget {
|
|||||||
|
|
||||||
class _CustomTabsState extends State<CustomTabs> {
|
class _CustomTabsState extends State<CustomTabs> {
|
||||||
final HomeController _homeController = Get.put(HomeController());
|
final HomeController _homeController = Get.put(HomeController());
|
||||||
int currentTabIndex = 1;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_homeController.tabController.addListener(listen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void listen() {
|
|
||||||
_homeController.initialIndex.value = _homeController.tabController.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onTap(int index) {
|
void onTap(int index) {
|
||||||
feedBack();
|
feedBack();
|
||||||
@ -271,34 +264,30 @@ class _CustomTabsState extends State<CustomTabs> {
|
|||||||
_homeController.tabController.index = index;
|
_homeController.tabController.index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
_homeController.tabController.removeListener(listen);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 44,
|
height: 44,
|
||||||
margin: const EdgeInsets.only(top: 4),
|
margin: const EdgeInsets.only(top: 4),
|
||||||
child: ListView.separated(
|
child: Obx(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14.0),
|
() => ListView.separated(
|
||||||
scrollDirection: Axis.horizontal,
|
padding: const EdgeInsets.symmetric(horizontal: 14.0),
|
||||||
itemCount: _homeController.tabs.length,
|
scrollDirection: Axis.horizontal,
|
||||||
separatorBuilder: (BuildContext context, int index) {
|
itemCount: _homeController.tabs.length,
|
||||||
return const SizedBox(width: 10);
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
},
|
return const SizedBox(width: 10);
|
||||||
itemBuilder: (BuildContext context, int index) {
|
},
|
||||||
String label = _homeController.tabs[index]['label'];
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return Obx(
|
String label = _homeController.tabs[index]['label'];
|
||||||
() => CustomChip(
|
return Obx(
|
||||||
onTap: () => onTap(index),
|
() => CustomChip(
|
||||||
label: label,
|
onTap: () => onTap(index),
|
||||||
selected: index == _homeController.initialIndex.value,
|
label: label,
|
||||||
),
|
selected: index == _homeController.initialIndex.value,
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
90
lib/pages/setting/pages/home_tabbar_set.dart
Normal file
90
lib/pages/setting/pages/home_tabbar_set.dart
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:pilipala/models/common/tab_type.dart';
|
||||||
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
|
class TabbarSetPage extends StatefulWidget {
|
||||||
|
const TabbarSetPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<TabbarSetPage> createState() => _TabbarSetPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TabbarSetPageState extends State<TabbarSetPage> {
|
||||||
|
Box settingStorage = GStrorage.setting;
|
||||||
|
late List defaultTabs;
|
||||||
|
late List<String> tabbarSort;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
defaultTabs = tabsConfig;
|
||||||
|
tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort,
|
||||||
|
defaultValue: ['live', 'rcmd', 'hot', 'bangumi']);
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveEdit() {
|
||||||
|
List<String> sortedTabbar = defaultTabs
|
||||||
|
.where((i) => tabbarSort.contains((i['type'] as TabType).id))
|
||||||
|
.map<String>((i) => (i['type'] as TabType).id)
|
||||||
|
.toList();
|
||||||
|
if (sortedTabbar.isEmpty) {
|
||||||
|
SmartDialog.showToast('请至少设置一项!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
settingStorage.put(SettingBoxKey.tabbarSort, sortedTabbar);
|
||||||
|
SmartDialog.showToast('保存成功,下次启动时生效');
|
||||||
|
}
|
||||||
|
|
||||||
|
void onReorder(int oldIndex, int newIndex) {
|
||||||
|
setState(() {
|
||||||
|
if (newIndex > oldIndex) {
|
||||||
|
newIndex -= 1;
|
||||||
|
}
|
||||||
|
final tabsItem = defaultTabs.removeAt(oldIndex);
|
||||||
|
defaultTabs.insert(newIndex, tabsItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final listTiles = [
|
||||||
|
for (int i = 0; i < defaultTabs.length; i++) ...[
|
||||||
|
CheckboxListTile(
|
||||||
|
key: Key(defaultTabs[i]['label']),
|
||||||
|
value: tabbarSort.contains((defaultTabs[i]['type'] as TabType).id),
|
||||||
|
onChanged: (bool? newValue) {
|
||||||
|
String tabTypeId = (defaultTabs[i]['type'] as TabType).id;
|
||||||
|
if (!newValue!) {
|
||||||
|
tabbarSort.remove(tabTypeId);
|
||||||
|
} else {
|
||||||
|
tabbarSort.add(tabTypeId);
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
title: Text(defaultTabs[i]['label']),
|
||||||
|
secondary: const Icon(Icons.drag_indicator_rounded),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Tabbar编辑'),
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: () => saveEdit(), child: const Text('保存')),
|
||||||
|
const SizedBox(width: 12)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: ReorderableListView(
|
||||||
|
onReorder: onReorder,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
footer: SizedBox(
|
||||||
|
height: MediaQuery.of(context).padding.bottom + 30,
|
||||||
|
),
|
||||||
|
children: listTiles,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -254,6 +254,11 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
onTap: () => Get.toNamed('/fontSizeSetting'),
|
onTap: () => Get.toNamed('/fontSizeSetting'),
|
||||||
title: Text('字体大小', style: titleStyle),
|
title: Text('字体大小', style: titleStyle),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
dense: false,
|
||||||
|
onTap: () => Get.toNamed('/tabbarSetting'),
|
||||||
|
title: Text('首页tabbar', style: titleStyle),
|
||||||
|
),
|
||||||
if (Platform.isAndroid)
|
if (Platform.isAndroid)
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
|
@ -36,6 +36,7 @@ import '../pages/setting/index.dart';
|
|||||||
import '../pages/setting/pages/color_select.dart';
|
import '../pages/setting/pages/color_select.dart';
|
||||||
import '../pages/setting/pages/display_mode.dart';
|
import '../pages/setting/pages/display_mode.dart';
|
||||||
import '../pages/setting/pages/font_size_select.dart';
|
import '../pages/setting/pages/font_size_select.dart';
|
||||||
|
import '../pages/setting/pages/home_tabbar_set.dart';
|
||||||
import '../pages/setting/pages/play_speed_set.dart';
|
import '../pages/setting/pages/play_speed_set.dart';
|
||||||
import '../pages/setting/play_setting.dart';
|
import '../pages/setting/play_setting.dart';
|
||||||
import '../pages/setting/privacy_setting.dart';
|
import '../pages/setting/privacy_setting.dart';
|
||||||
@ -112,6 +113,8 @@ class Routes {
|
|||||||
//
|
//
|
||||||
CustomGetPage(name: '/blackListPage', page: () => const BlackListPage()),
|
CustomGetPage(name: '/blackListPage', page: () => const BlackListPage()),
|
||||||
CustomGetPage(name: '/colorSetting', page: () => const ColorSelectPage()),
|
CustomGetPage(name: '/colorSetting', page: () => const ColorSelectPage()),
|
||||||
|
// 首页tabbar
|
||||||
|
CustomGetPage(name: '/tabbarSetting', page: () => const TabbarSetPage()),
|
||||||
CustomGetPage(
|
CustomGetPage(
|
||||||
name: '/fontSizeSetting', page: () => const FontSizeSelectPage()),
|
name: '/fontSizeSetting', page: () => const FontSizeSelectPage()),
|
||||||
// 屏幕帧率
|
// 屏幕帧率
|
||||||
|
@ -144,7 +144,8 @@ class SettingBoxKey {
|
|||||||
customRows = 'customRows', // 自定义列
|
customRows = 'customRows', // 自定义列
|
||||||
enableMYBar = 'enableMYBar',
|
enableMYBar = 'enableMYBar',
|
||||||
hideSearchBar = 'hideSearchBar', // 收起顶栏
|
hideSearchBar = 'hideSearchBar', // 收起顶栏
|
||||||
hideTabBar = 'hideTabBar'; // 收起底栏
|
hideTabBar = 'hideTabBar', // 收起底栏
|
||||||
|
tabbarSort = 'tabbarSort'; // 首页tabbar
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalCacheKey {
|
class LocalCacheKey {
|
||||||
|
Reference in New Issue
Block a user