opt: search panel layout

This commit is contained in:
guozhigq
2024-10-26 15:10:30 +08:00
parent b6e6cc1735
commit baa6a644e3
8 changed files with 134 additions and 77 deletions

View File

@ -3,14 +3,9 @@ import 'package:pilipala/common/constants.dart';
import 'skeleton.dart';
class MediaBangumiSkeleton extends StatefulWidget {
class MediaBangumiSkeleton extends StatelessWidget {
const MediaBangumiSkeleton({super.key});
@override
State<MediaBangumiSkeleton> createState() => _MediaBangumiSkeletonState();
}
class _MediaBangumiSkeletonState extends State<MediaBangumiSkeleton> {
@override
Widget build(BuildContext context) {
Color bgColor = Theme.of(context).colorScheme.onInverseSurface;
@ -35,25 +30,25 @@ class _MediaBangumiSkeletonState extends State<MediaBangumiSkeleton> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color: Theme.of(context).colorScheme.onInverseSurface,
color: bgColor,
width: 200,
height: 20,
margin: const EdgeInsets.only(bottom: 15),
),
Container(
color: Theme.of(context).colorScheme.onInverseSurface,
color: bgColor,
width: 150,
height: 13,
margin: const EdgeInsets.only(bottom: 5),
),
Container(
color: Theme.of(context).colorScheme.onInverseSurface,
color: bgColor,
width: 150,
height: 13,
margin: const EdgeInsets.only(bottom: 5),
),
Container(
color: Theme.of(context).colorScheme.onInverseSurface,
color: bgColor,
width: 150,
height: 13,
),
@ -64,7 +59,7 @@ class _MediaBangumiSkeletonState extends State<MediaBangumiSkeleton> {
decoration: BoxDecoration(
borderRadius:
const BorderRadius.all(Radius.circular(20)),
color: Theme.of(context).colorScheme.onInverseSurface,
color: bgColor,
),
),
],

View File

@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import '../constants.dart';
class UserListSkeleton extends StatelessWidget {
const UserListSkeleton({super.key});
@override
Widget build(BuildContext context) {
Color bgColor = Theme.of(context).colorScheme.onInverseSurface;
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace, vertical: 7),
child: Row(
children: [
ClipOval(
child: Container(width: 42, height: 42, color: bgColor),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(color: bgColor, width: 60, height: 13),
const SizedBox(width: 10),
Container(color: bgColor, width: 40, height: 13),
],
),
const SizedBox(height: 6),
Container(
color: bgColor,
width: 100,
height: 13,
),
],
),
),
],
));
}
}

View File

@ -31,7 +31,7 @@ class SearchPanelController extends GetxController {
tids: searchType!.type != 'video' ? null : tids.value,
);
if (result['status']) {
if (type == 'onRefresh') {
if (type == 'init') {
resultList.value = result['data'].list ?? [];
} else {
resultList.addAll(result['data'].list ?? []);
@ -44,7 +44,7 @@ class SearchPanelController extends GetxController {
Future onRefresh() async {
page.value = 1;
await onSearch(type: 'onRefresh');
await onSearch();
}
// 返回顶部并刷新

View File

@ -4,6 +4,7 @@ import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/media_bangumi.dart';
import 'package:pilipala/common/skeleton/user_list.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/models/common/search_type.dart';
@ -81,11 +82,11 @@ class _SearchPanelState extends State<SearchPanel>
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data != null) {
Map data = snapshot.data;
Map? data = snapshot.data;
if (data != null && data['status']) {
var ctr = _searchPanelController;
RxList list = ctr.resultList;
if (data['status']) {
if (list.isNotEmpty) {
return Obx(() {
switch (widget.searchType) {
case SearchType.video:
@ -110,21 +111,18 @@ class _SearchPanelState extends State<SearchPanel>
});
} else {
return HttpError(
errMsg: data['msg'],
fn: () {
setState(() {
_searchPanelController.onSearch();
});
},
errMsg: '没有数据',
isShowBtn: false,
fn: () => {},
isInSliver: false,
);
}
} else {
return HttpError(
errMsg: '没有相关数据',
errMsg: data?['msg'] ?? '请求异常',
fn: () {
setState(() {
_searchPanelController.onSearch();
_futureBuilderFuture = _searchPanelController.onRefresh();
});
},
isInSliver: false,
@ -143,7 +141,7 @@ class _SearchPanelState extends State<SearchPanel>
case SearchType.media_bangumi:
return const MediaBangumiSkeleton();
case SearchType.bili_user:
return const VideoCardHSkeleton();
return const UserListSkeleton();
case SearchType.live_room:
return const VideoCardHSkeleton();
default:

View File

@ -1,12 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/badge.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/models/bangumi/info.dart';
import 'package:pilipala/models/common/search_type.dart';
import 'package:pilipala/utils/route_push.dart';
import 'package:pilipala/utils/utils.dart';
@ -30,8 +25,8 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) {
// });
},
child: Padding(
padding: const EdgeInsets.fromLTRB(
StyleString.safeSpace, 7, StyleString.safeSpace, 7),
padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace, vertical: 7),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/utils.dart';
@ -20,7 +21,8 @@ Widget searchUserPanel(BuildContext context, ctr, list) {
onTap: () => Get.toNamed('/member?mid=${i.mid}',
arguments: {'heroTag': heroTag, 'face': i.upic}),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace, vertical: 7),
child: Row(
children: [
Hero(

View File

@ -3,6 +3,7 @@ import 'package:get/get.dart';
import 'package:pilipala/models/common/search_type.dart';
import 'package:pilipala/pages/search_panel/index.dart';
import 'controller.dart';
import 'widget/tab_bar.dart';
class SearchResultPage extends StatefulWidget {
const SearchResultPage({super.key});
@ -29,6 +30,17 @@ class _SearchResultPageState extends State<SearchResultPage>
);
}
// tab点击事件
void _onTap(int index) {
if (index == _searchResultController.tabIndex) {
Get.find<SearchPanelController>(
tag: SearchType.values[index].type +
_searchResultController.keyword!)
.animateToTop();
}
_searchResultController.tabIndex = index;
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -55,50 +67,10 @@ class _SearchResultPageState extends State<SearchResultPage>
body: Column(
children: [
const SizedBox(height: 4),
Container(
width: double.infinity,
padding: const EdgeInsets.only(left: 8),
color: Theme.of(context).colorScheme.surface,
child: Theme(
data: ThemeData(
splashColor: Colors.transparent, // 点击时的水波纹颜色设置为透明
highlightColor: Colors.transparent, // 点击时的背景高亮颜色设置为透明
),
child: Obx(
() => (TabBar(
controller: _tabController,
tabs: [
for (var i in _searchResultController.searchTabs)
Tab(text: "${i['label']} ${i['count'] ?? ''}")
],
isScrollable: true,
indicatorWeight: 0,
indicatorPadding:
const EdgeInsets.symmetric(horizontal: 3, vertical: 8),
indicator: BoxDecoration(
color: Theme.of(context).colorScheme.secondaryContainer,
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
indicatorSize: TabBarIndicatorSize.tab,
labelColor:
Theme.of(context).colorScheme.onSecondaryContainer,
labelStyle: const TextStyle(fontSize: 13),
dividerColor: Colors.transparent,
unselectedLabelColor: Theme.of(context).colorScheme.outline,
tabAlignment: TabAlignment.start,
onTap: (index) {
if (index == _searchResultController.tabIndex) {
Get.find<SearchPanelController>(
tag: SearchType.values[index].type +
_searchResultController.keyword!)
.animateToTop();
}
_searchResultController.tabIndex = index;
},
)),
),
),
TabBarWidget(
onTap: _onTap,
tabController: _tabController!,
searchResultCtr: _searchResultController,
),
Expanded(
child: TabBarView(

View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/pages/search_result/index.dart';
class TabBarWidget extends StatelessWidget {
final Function(int) onTap;
final TabController tabController;
final SearchResultController searchResultCtr;
const TabBarWidget({
required this.onTap,
required this.tabController,
required this.searchResultCtr,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
ColorScheme colorScheme = Theme.of(context).colorScheme;
Color transparent = Colors.transparent;
return Container(
width: double.infinity,
padding: const EdgeInsets.only(left: 8),
color: colorScheme.surface,
child: Theme(
data: ThemeData(splashColor: transparent, highlightColor: transparent),
child: Obx(
() => TabBar(
controller: tabController,
tabs: [
for (var i in searchResultCtr.searchTabs)
Tab(text: "${i['label']} ${i['count'] ?? ''}"),
],
isScrollable: true,
indicatorPadding:
const EdgeInsets.symmetric(horizontal: 3, vertical: 8),
indicator: BoxDecoration(
color: colorScheme.secondaryContainer,
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
indicatorSize: TabBarIndicatorSize.tab,
labelColor: colorScheme.onSecondaryContainer,
labelStyle: const TextStyle(fontSize: 13),
dividerColor: transparent,
unselectedLabelColor: colorScheme.outline,
tabAlignment: TabAlignment.start,
onTap: onTap,
),
),
),
);
}
}