初始化
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/assets_tool/images.dart';
|
||||
import 'package:hgdj/config/config.dart';
|
||||
import 'package:hgdj/hj_utils/screen.dart';
|
||||
import 'package:hgdj/tools_base/widget/net_image_widget.dart';
|
||||
|
||||
import '../../../../tools_base/widget/marquee_widget.dart';
|
||||
import '../../../mine/welfare/sign_daily_page.dart';
|
||||
import '../../home_main_logic.dart';
|
||||
import '../search_main_page.dart';
|
||||
|
||||
class CommonSearchBarView extends StatefulWidget {
|
||||
final HomeMainLogic? logic;
|
||||
|
||||
const CommonSearchBarView({super.key, this.logic});
|
||||
|
||||
@override
|
||||
State<CommonSearchBarView> createState() => _CommonSearchBarViewState();
|
||||
}
|
||||
|
||||
class _CommonSearchBarViewState extends State<CommonSearchBarView> {
|
||||
/// 当前轮播展示的搜索热词,点击搜索框时带入搜索页
|
||||
String? _searchText;
|
||||
|
||||
static final _hintStyle =
|
||||
TextStyle(color: Colors.white.withValues(alpha: 0.5), fontSize: 14);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (Config.searchHints.isNotEmpty) _searchText = Config.searchHints.first;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
16.w.sizeBoxW,
|
||||
Image.asset('place_holder_logo.webp'.commonImgPath, height: 28),
|
||||
8.sizeBoxW,
|
||||
Expanded(child: _buildSearchBox()),
|
||||
12.sizeBoxW,
|
||||
if (Config.signIcon != null && Config.signIcon!.isNotEmpty)
|
||||
_buildSignIcon(),
|
||||
12.sizeBoxW,
|
||||
_buildMenuIcon(),
|
||||
16.sizeBoxW,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 中间搜索框(热词轮播)
|
||||
Widget _buildSearchBox() {
|
||||
return InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () => Get.to(SearchMainPage(searchText: _searchText)),
|
||||
child: Container(
|
||||
height: 36,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xff1E1C1D),
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
),
|
||||
child: Config.searchHints.isEmpty
|
||||
? Text('请输入关键字', style: _hintStyle)
|
||||
: MarqueeWidget(
|
||||
count: Config.searchHints.length,
|
||||
onIndexChanged: (index) =>
|
||||
_searchText = Config.searchHints[index],
|
||||
itemBuilder: (_, index) => Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(Config.searchHints[index], style: _hintStyle),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 签到入口
|
||||
Widget _buildSignIcon() {
|
||||
return InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () => Get.to(SignDailyPage()),
|
||||
child: NetworkImageLoader(
|
||||
imageUrl: Config.signIcon,
|
||||
width: 20,
|
||||
borderRadius: 0,
|
||||
placeHolderWidget: const SizedBox(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 右侧菜单入口
|
||||
Widget _buildMenuIcon() {
|
||||
return InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () => widget.logic?.openEndDrawer(),
|
||||
child: Image.asset('acg_menu.png'.acgImgPath,
|
||||
width: 24, color: const Color(0xff989898)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/assets_tool/images.dart';
|
||||
import 'package:hgdj/hj_utils/screen.dart';
|
||||
import 'package:hgdj/tools_base/toast.dart';
|
||||
|
||||
import '../video_all_type_page.dart';
|
||||
|
||||
final class SearchAppBar extends StatefulWidget {
|
||||
final Function(String keywork) onSubmitted;
|
||||
final bool? isClose;
|
||||
final String? searchText;
|
||||
|
||||
/// 外部持有的输入控制器:搜索主页由 logic 持有,点热搜词/历史项后好回填。
|
||||
/// 不传则本组件自建自销——每个 SearchAppBar 各用各的,不再共享全局单例
|
||||
final TextEditingController? controller;
|
||||
|
||||
const SearchAppBar({
|
||||
super.key,
|
||||
required this.onSubmitted,
|
||||
this.searchText,
|
||||
this.isClose,
|
||||
this.controller,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SearchAppBar> createState() => _SearchAppBarState();
|
||||
}
|
||||
|
||||
class _SearchAppBarState extends State<SearchAppBar> {
|
||||
late final searchTextCtr = widget.controller ?? TextEditingController();
|
||||
late bool showDeleteIcon;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final initText = widget.searchText ?? "";
|
||||
showDeleteIcon = initText.isNotEmpty;
|
||||
// 控制器不再跨路由共享,此处赋值不会 markNeedsBuild 别的路由,无需延到首帧后
|
||||
searchTextCtr.text = initText;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// 只释放自己 new 的;外部传进来的归调用方管
|
||||
if (widget.controller == null) searchTextCtr.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: screen.paddingTop),
|
||||
color: Colors.black,
|
||||
child: Container(
|
||||
height: 56,
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
showDeleteIcon = false;
|
||||
searchTextCtr.text = "";
|
||||
});
|
||||
Get.back();
|
||||
},
|
||||
child: Image.asset(
|
||||
'common_back.png'.commonImgPath,
|
||||
width: 22,
|
||||
),
|
||||
),
|
||||
10.sizeBoxW,
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 32,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
alignment: Alignment.centerLeft,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0x11FFFFFF),
|
||||
borderRadius: BorderRadius.circular(20)),
|
||||
child: Row(
|
||||
children: [
|
||||
InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () {
|
||||
Get.to(() => VideoAllTypePage(), opaque: false);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
2.sizeBoxW,
|
||||
Image.asset('libary_search.webp'.homePath, width: 20),
|
||||
6.sizeBoxW,
|
||||
Text(
|
||||
'片库',
|
||||
style: TextStyle(
|
||||
color: Color(0xFFF68804),
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
6.sizeBoxW,
|
||||
Container(
|
||||
height: 12,
|
||||
width: 1,
|
||||
color: Colors.white.withValues(alpha: .05),
|
||||
),
|
||||
6.sizeBoxW,
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: searchTextCtr,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: .8),
|
||||
fontSize: 14,
|
||||
height: 1),
|
||||
onSubmitted: (value) => widget.onSubmitted(value),
|
||||
onChanged: (value) {
|
||||
if (value.isNotEmpty == true) {
|
||||
setState(() {
|
||||
showDeleteIcon = true;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
showDeleteIcon = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
textInputAction: TextInputAction.search,
|
||||
decoration: InputDecoration(
|
||||
hintText: '搜索关键词',
|
||||
hintStyle: TextStyle(
|
||||
color: Colors.white.withValues(alpha: .55),
|
||||
fontSize: 14,
|
||||
height: 1),
|
||||
border: InputBorder.none,
|
||||
isDense: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
),
|
||||
),
|
||||
12.sizeBoxW,
|
||||
Visibility(
|
||||
visible: showDeleteIcon,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
searchTextCtr.text = "";
|
||||
showDeleteIcon = false;
|
||||
});
|
||||
},
|
||||
child: Image.asset('search_close.webp'.homePath,
|
||||
width: 18),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
10.sizeBoxW,
|
||||
InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () {
|
||||
if (widget.isClose == true) {
|
||||
setState(() {
|
||||
showDeleteIcon = false;
|
||||
});
|
||||
}
|
||||
if (searchTextCtr.text.isEmpty == true) {
|
||||
showToast("请输入关键字");
|
||||
return;
|
||||
}
|
||||
widget.onSubmitted(searchTextCtr.text);
|
||||
},
|
||||
child: Text(
|
||||
"搜索",
|
||||
style: TextStyle(
|
||||
color: Color(0x73FFFFFF),
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hgdj/assets_tool/images.dart';
|
||||
import 'package:hgdj/hj_utils/screen.dart';
|
||||
|
||||
/// 搜索历史区:纯展示,数据与增删由 SearchMainLogic 持有
|
||||
class SearchHistoryView extends StatefulWidget {
|
||||
const SearchHistoryView({
|
||||
super.key,
|
||||
required this.histories,
|
||||
required this.onHistoryClick,
|
||||
required this.onClearAll,
|
||||
});
|
||||
|
||||
final List<String> histories;
|
||||
final Function(String keyword) onHistoryClick;
|
||||
final VoidCallback onClearAll;
|
||||
|
||||
@override
|
||||
State<SearchHistoryView> createState() => _SearchHistoryViewState();
|
||||
}
|
||||
|
||||
class _SearchHistoryViewState extends State<SearchHistoryView> {
|
||||
/// 折叠状态最多展示几条
|
||||
static const _collapsedMax = 8;
|
||||
|
||||
bool isExpandData = false;
|
||||
|
||||
int get itemCount {
|
||||
final total = widget.histories.length;
|
||||
return isExpandData ? total : min(_collapsedMax, total);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.histories.isEmpty) return const SizedBox.shrink();
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(left: 12, right: 12, top: 12),
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
'历史记录',
|
||||
style: TextStyle(
|
||||
color: Color(0xE5FFFFFF),
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: widget.onClearAll,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(1),
|
||||
child: Image.asset("history_delete.webp".homePath,
|
||||
width: 18, height: 18),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
12.sizeBoxH,
|
||||
GridView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: itemCount,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 4,
|
||||
childAspectRatio: 79 / 34,
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 12,
|
||||
),
|
||||
itemBuilder: (_, index) =>
|
||||
_buildHistoryItem(widget.histories[index]),
|
||||
),
|
||||
_buildMoreRecord(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHistoryItem(String tag) {
|
||||
return GestureDetector(
|
||||
onTap: () => widget.onHistoryClick(tag),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0x0DFFFFFF),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
child: Text(
|
||||
tag,
|
||||
maxLines: 1,
|
||||
style: const TextStyle(color: Color(0xE5FFFFFF), fontSize: 12),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 「查看 / 收起完整记录」按钮:少于 [_collapsedMax] 条时不显示
|
||||
Widget _buildMoreRecord() {
|
||||
if (widget.histories.length < _collapsedMax) {
|
||||
return const SizedBox();
|
||||
}
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
alignment: Alignment.topCenter,
|
||||
child: InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () => setState(() => isExpandData = !isExpandData),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
isExpandData ? "收起完整记录" : "查看完整记录",
|
||||
style: const TextStyle(color: Color(0xff989898), fontSize: 12),
|
||||
),
|
||||
4.sizeBoxW,
|
||||
Transform.rotate(
|
||||
angle: isExpandData ? pi : 0,
|
||||
child: Image.asset(
|
||||
"arrow_down.png".commonImgPath,
|
||||
width: 12,
|
||||
color: const Color(0xffDCDCDC),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:hgdj/config/config.dart';
|
||||
import 'package:hgdj/hj_utils/screen.dart';
|
||||
|
||||
class SearchHotTagView extends StatelessWidget {
|
||||
final Function(String keyword) onTagClick;
|
||||
|
||||
const SearchHotTagView({super.key, required this.onTagClick});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tags = Config.hotWords;
|
||||
if (tags.isEmpty) return const SizedBox.shrink();
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(left: 12, right: 12, top: 18),
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'大家都在搜',
|
||||
style: TextStyle(
|
||||
color: Color(0xE5FFFFFF),
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
12.h.sizeBoxH,
|
||||
GridView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: tags.length,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 4,
|
||||
childAspectRatio: 79 / 34,
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 12,
|
||||
),
|
||||
itemBuilder: (_, index) => _buildTagItem(tags[index]),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTagItem(String tag) {
|
||||
return GestureDetector(
|
||||
onTap: () => onTagClick(tag),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0x0DFFFFFF),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
child: Text(
|
||||
tag,
|
||||
maxLines: 1,
|
||||
style: const TextStyle(
|
||||
color: Color(0xE5FFFFFF),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/assets_tool/app_colors.dart';
|
||||
import 'package:hgdj/extension/extensions.dart';
|
||||
import 'package:hgdj/hj_model/video_model.dart';
|
||||
import 'package:hgdj/hj_utils/screen.dart';
|
||||
import 'package:hgdj/tools_base/widget/net_image_widget.dart';
|
||||
|
||||
import '../../../community/community_tag_page/community_tag_page.dart';
|
||||
|
||||
class TopicItem extends StatelessWidget {
|
||||
final TagsBean model;
|
||||
final bool isSelect;
|
||||
final Function()? onTap;
|
||||
|
||||
const TopicItem(
|
||||
{super.key, required this.model, this.onTap, this.isSelect = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () {
|
||||
if (onTap != null) {
|
||||
onTap!();
|
||||
} else {
|
||||
Get.to(() => CommunityTagDetailPage(model: model));
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
height: 66,
|
||||
padding: EdgeInsets.only(left: 12.w, right: 12.w),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0x0DFFFFFF),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
NetworkImageLoader(
|
||||
imageUrl: model.coverImg ?? '',
|
||||
width: 38,
|
||||
height: 38,
|
||||
borderRadius: 3,
|
||||
),
|
||||
10.sizeBoxW,
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'#${model.name}',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
// 4.sizeBoxH,
|
||||
Text(
|
||||
'${model.vidCount?.countStr}个帖子 ${model.playCount.countStr}浏览 ${model.followCount.countStr}关注',
|
||||
style: TextStyle(color: Color(0x73FFFFFF), fontSize: 12),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
12.sizeBoxW,
|
||||
Container(
|
||||
width: 52,
|
||||
height: 26,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelect ? AppColors.actionRed : Color(0x33FFFFFF),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
child: Text(
|
||||
isSelect ? '已选' : "选择",
|
||||
style: TextStyle(
|
||||
color: isSelect ? Color(0xffffffff) : Color(0xffDCDCDC),
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user