diff --git a/lib/pages/goods_details_page/goods_details_page.dart b/lib/pages/goods_details_page/goods_details_page.dart index 7e9a135..a0579c1 100644 --- a/lib/pages/goods_details_page/goods_details_page.dart +++ b/lib/pages/goods_details_page/goods_details_page.dart @@ -6,6 +6,7 @@ import 'package:provider/provider.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:zhiying_base_widget/pages/goods_details_page/bloc/goods_details_page_bloc.dart'; import 'package:zhiying_base_widget/pages/goods_details_page/bloc/goods_details_page_repository.dart'; +import 'package:zhiying_base_widget/pages/goods_details_page/goods_details_page_sk.dart'; import 'package:zhiying_base_widget/pages/goods_details_page/notifier/goods_details_page_notifier.dart'; import 'package:zhiying_base_widget/widgets/goods_details/footer/goods_details_footer_widget.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; @@ -120,7 +121,7 @@ class _GoodsDetailsContainerState extends State { if (state is GoodsDetailsPageLoadedState) { return _getMainWidget(state?.model); } - return _getMainWidget(null); + return GoodsDetailsPageSkeleton(); }, ), ), diff --git a/lib/pages/goods_details_page/goods_details_page_sk.dart b/lib/pages/goods_details_page/goods_details_page_sk.dart new file mode 100644 index 0000000..f8dcdc4 --- /dev/null +++ b/lib/pages/goods_details_page/goods_details_page_sk.dart @@ -0,0 +1,46 @@ +import 'package:zhiying_base_widget/widgets/goods_details/coupon/counpon_sk.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/footer/goods_details_footer_sk.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/slide_banner/goods_details_slide_banner_sk.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/store/store_sk.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/upgrade_tip/upgrade_tip_sk.dart'; +import 'package:zhiying_comm/util/shimmer_util.dart'; +import 'package:flutter/material.dart'; + +class GoodsDetailsPageSkeleton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Stack( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// 轮播图 + GoodsDetailsSlideBannerSkeleton(), + SizedBox(height: 12.5), + + /// 更新 + UpgradeTipSkeleton(), + + /// 商店 + StoreSkeleton(), + + /// 优惠卷 + CounponSkeleton(), + SizedBox(height: 15), + + /// 图片 + ShimmerUtil.getShimmerWidget(width: double.infinity, height: 40), + ShimmerUtil.getShimmerWidget(width: double.infinity, height: 40), + ], + ), + Align( + alignment: Alignment.bottomCenter, + child: GoodsDetailsFooterSkeleton(), + ) + ], + ), + ); + } +} diff --git a/lib/pages/search_page/bloc/search_bloc.dart b/lib/pages/search_page/bloc/search_bloc.dart index ec27217..160c828 100644 --- a/lib/pages/search_page/bloc/search_bloc.dart +++ b/lib/pages/search_page/bloc/search_bloc.dart @@ -37,6 +37,12 @@ class SearchBloc extends Bloc { /// 初始化 Stream _mapInitEventToState(SearchInitEvent event) async* { + /// 获取缓存的数据 + var cache = await repository.fetchCachedData(event.model); + if(!EmptyUtil.isEmpty(cache)){ + print('缓存数据'); + yield SearchLoadedState(model: cache); + } /// 获取数据 var result = await repository.fetchInit(event.model); Logger.log('result = ${result.toString()}'); diff --git a/lib/pages/search_page/bloc/search_repository.dart b/lib/pages/search_page/bloc/search_repository.dart index ebd6db2..6f78df2 100644 --- a/lib/pages/search_page/bloc/search_repository.dart +++ b/lib/pages/search_page/bloc/search_repository.dart @@ -5,7 +5,7 @@ class SearchRepository { /// 获取网络数据 Future>> fetchInit(final Map model) async { - var result = await NetUtil.post('/api/v1/mod/pub.flutter.search_index', method: NetMethod.GET); + var result = await NetUtil.post('/api/v1/mod/pub.flutter.search_index', method: NetMethod.GET, cache: true); if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result)) { try { _pageData = List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['mod_list']); @@ -18,7 +18,15 @@ class SearchRepository { } /// 获取缓存数据 - Future>> fetchCachedData(final Map model) {} + Future>> fetchCachedData(final Map model) async{ + var result = await NetUtil.getRequestCachedData('/api/v1/mod/pub.flutter.search_index'); + try { + if (!EmptyUtil.isEmpty(result)) { + return List.from(result['mod_list']); + } + }catch(e){} + return null; + } /// 搜索的方法 Future fetchSearchSubmit(final String model) async { diff --git a/lib/pages/search_page/item/bloc/search_item_page_bloc.dart b/lib/pages/search_page/item/bloc/search_item_page_bloc.dart new file mode 100644 index 0000000..2f06e61 --- /dev/null +++ b/lib/pages/search_page/item/bloc/search_item_page_bloc.dart @@ -0,0 +1,47 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:zhiying_base_widget/pages/search_page/item/bloc/search_item_repostitory.dart'; +import 'package:zhiying_comm/util/empty_util.dart'; + +part 'search_item_page_event.dart'; + +part 'search_item_page_state.dart'; + +class SearchItemPageBloc extends Bloc { + // SearchItemPageBloc() : super(SearchItemPageInitial()); + + SearchItemRepository repository; + + SearchItemPageBloc({@required this.repository}); + + @override + // TODO: implement initialState + SearchItemPageState get initialState => SearchItemPageInitial(); + + @override + Stream mapEventToState( + SearchItemPageEvent event, + ) async* { + /// 初始化 + if (event is SearchItemPageInitEvent) { + yield* _mapInitEventToState(event); + } + } + + /// 初始化 + Stream _mapInitEventToState(SearchItemPageInitEvent event) async* { + var cache = await repository.fetchCachedData(event.model); + if(!EmptyUtil.isEmpty(cache)){ + yield SearchItemLoadedState(model: cache); + } + var result = await repository.fetchInit(event.model); + if (!EmptyUtil.isEmpty(result)) { + yield SearchItemLoadedState(model: result); + } else { + yield SearchItemErrorState(); + } + } +} diff --git a/lib/pages/search_page/item/bloc/search_item_page_event.dart b/lib/pages/search_page/item/bloc/search_item_page_event.dart new file mode 100644 index 0000000..a41781a --- /dev/null +++ b/lib/pages/search_page/item/bloc/search_item_page_event.dart @@ -0,0 +1,15 @@ +part of 'search_item_page_bloc.dart'; + +abstract class SearchItemPageEvent extends Equatable { + const SearchItemPageEvent(); +} + +/// 初始化 +class SearchItemPageInitEvent extends SearchItemPageEvent { + final Map model; + + const SearchItemPageInitEvent({@required this.model}); + + @override + List get props => [this.model]; +} diff --git a/lib/pages/search_page/item/bloc/search_item_page_state.dart b/lib/pages/search_page/item/bloc/search_item_page_state.dart new file mode 100644 index 0000000..76ab2b4 --- /dev/null +++ b/lib/pages/search_page/item/bloc/search_item_page_state.dart @@ -0,0 +1,26 @@ +part of 'search_item_page_bloc.dart'; + +abstract class SearchItemPageState extends Equatable { + const SearchItemPageState(); + + @override + List get props => []; +} + +class SearchItemPageInitial extends SearchItemPageState { + @override + List get props => []; +} + +/// 数据加载成功 +class SearchItemLoadedState extends SearchItemPageState { + List> model; + + SearchItemLoadedState({this.model}); + + @override + List get props => [this.model]; +} + +/// 数据加载失败 +class SearchItemErrorState extends SearchItemPageState {} diff --git a/lib/pages/search_page/item/bloc/search_item_repostitory.dart b/lib/pages/search_page/item/bloc/search_item_repostitory.dart new file mode 100644 index 0000000..d2c2d96 --- /dev/null +++ b/lib/pages/search_page/item/bloc/search_item_repostitory.dart @@ -0,0 +1,34 @@ +import 'package:zhiying_comm/zhiying_comm.dart'; + +class SearchItemRepository { + List> _pageData = []; + + /// 获取网络数据 + Future>> fetchInit(final Map model) async { + + String skip_identifier = model['skip_identifier']; + var result = await NetUtil.post('/api/v1/mod/$skip_identifier', method: NetMethod.GET, cache: true); + if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result)) { + try { + _pageData = List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['mod_list']); + return _pageData; + } catch (e) { + Logger.error(e.toString()); + } + } + return null; + } + + /// 获取缓存数据 + Future>> fetchCachedData(final Map model) async { + String skip_identifier = model['skip_identifier']; + var result = await NetUtil.getRequestCachedData('/api/v1/mod/$skip_identifier'); + try { + if (!EmptyUtil.isEmpty(result)) { + return List.from(result['mod_list']); + } + }catch(e){} + return null; + } + +} diff --git a/lib/pages/search_page/item/search_item_page.dart b/lib/pages/search_page/item/search_item_page.dart new file mode 100644 index 0000000..683e487 --- /dev/null +++ b/lib/pages/search_page/item/search_item_page.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:zhiying_base_widget/pages/search_page/item/bloc/search_item_page_bloc.dart'; +import 'package:zhiying_base_widget/pages/search_page/item/bloc/search_item_repostitory.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class SearchItemPage extends StatefulWidget { + final Map data; + + const SearchItemPage(this.data); + + @override + _SearchItemPageState createState() => _SearchItemPageState(); +} + +class _SearchItemPageState extends State with AutomaticKeepAliveClientMixin { + @override + bool get wantKeepAlive => true; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => SearchItemPageBloc(repository: SearchItemRepository())..add(SearchItemPageInitEvent(model: widget?.data)), + child: SearchItemPageContainer(), + ); + } +} + +class SearchItemPageContainer extends StatefulWidget { + @override + _SearchItemPageContainerState createState() => _SearchItemPageContainerState(); +} + +class _SearchItemPageContainerState extends State { + RefreshController _refreshController; + ScrollController _controller; + + @override + void initState() { + _refreshController = RefreshController(initialRefresh: false); + _controller = ScrollController(); + super.initState(); + } + + @override + void dispose() { + _refreshController?.dispose(); + _controller?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocConsumer( + listener: (BuildContext context, SearchItemPageState state) { + if (state is SearchItemErrorState) { + print('数据加载出错'); + } + }, + buildWhen: (previous, current) { + /// 数据加载出错不进行build + if (current is SearchItemErrorState) { + return false; + } + return true; + }, + builder: (context, state) { + print('currente state = $state'); + if (state is SearchItemLoadedState) { + return _getMainWidget(state?.model ?? null); + } + + /// 骨架屏幕 + return _getMainWidget(null); + }, + ); + } + + /// 主视图 + Widget _getMainWidget(List> datas) { + return Scaffold( + backgroundColor: HexColor.fromHex('#F9F9F9'), + body: SmartRefresher( + enablePullDown: false, + enablePullUp: true, + footer: ClassicFooter(), + header: WaterDropHeader(), + controller: _refreshController, + child: Container( + width: double.infinity, + child: CustomScrollView( + controller: _controller, + slivers: _createContentWidget(datas), + ))), + ); + } + + /// 构造整体视图 + List _createContentWidget(List> datas) { + List list = []; + int length = datas?.length ?? 0; + if (length > 0) { + for (int i = 0; i < datas.length; i++) { + WidgetModel item = WidgetModel.fromJson(Map.from(datas[i])); + print('item.modName ${item.modName}'); + list.addAll(WidgetFactory.create( + item.modName, + isSliver: true, + model: datas[i], + )); + } + } else { + list.add(SliverToBoxAdapter( + /// TODO 改成骨架图 + child: Container(child: Text('暂无数据~')), + )); + } + return list; + } +} diff --git a/lib/pages/search_page/notifier/search_tag_notifier.dart b/lib/pages/search_page/notifier/search_tag_notifier.dart new file mode 100644 index 0000000..3bc5745 --- /dev/null +++ b/lib/pages/search_page/notifier/search_tag_notifier.dart @@ -0,0 +1,59 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class SearchTagNotifier with ChangeNotifier { + ///文本标签集合 + List _tagList = []; + + /// 保存历史标签的sp key + static final String _SP_HOISTROY_KEY = 'hoistroyTag'; + + /// 最大存储条数 + static final int _MAX_COUNT = 10; + + /// 初始化历史搜索标签 + Future> getHistoryTag() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String jsonStr = prefs.getString(_SP_HOISTROY_KEY); + if (null != jsonStr && jsonStr.length > 0) { + final Map jsonMap = jsonDecode(jsonStr); + if (jsonMap != null && jsonMap.length > 0) { + jsonMap.forEach((key, value) => _tagList.insert(0, value)); + } + } + return _tagList; + } + + /// 添加搜索 + void addTag(String tag) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String jsonStr = prefs.getString(_SP_HOISTROY_KEY); + if (null != jsonStr && jsonStr.length > 0) { + final Map jsonMap = jsonDecode(jsonStr); + jsonMap[tag.toString()] = tag.toString(); + + _tagList.insert(0, tag); // 第一位 + if (_tagList.length > _MAX_COUNT) { + jsonMap.remove(_tagList[_tagList.length - 1]); + _tagList.removeAt(_tagList.length - 1); + } + + prefs.setString(_SP_HOISTROY_KEY, jsonEncode(jsonMap)); + } else { + prefs.setString(_SP_HOISTROY_KEY, jsonEncode({'${tag.toString()}': '${tag.toString()}'})); + _tagList.add(tag); + } + notifyListeners(); + } + + /// 请空 + void clear() async{ + SharedPreferences prefs = await SharedPreferences.getInstance(); + // String jsonStr = prefs.getString(_SP_HOISTROY_KEY); + prefs.setString(_SP_HOISTROY_KEY, ''); + _tagList.clear(); + notifyListeners(); + } +} diff --git a/lib/pages/search_page/search_page.dart b/lib/pages/search_page/search_page.dart index c6acf83..c332847 100644 --- a/lib/pages/search_page/search_page.dart +++ b/lib/pages/search_page/search_page.dart @@ -1,6 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/pages/search_page/notifier/search_tag_notifier.dart'; +import 'package:zhiying_base_widget/pages/search_think_page/bloc/search_think_bloc.dart'; +import 'package:zhiying_base_widget/pages/search_think_page/bloc/search_think_repository.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:provider/provider.dart'; import 'bloc/search_bloc.dart'; import 'bloc/search_repository.dart'; @@ -15,9 +19,30 @@ class SearchPage extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (_) => SearchBloc(repository: SearchRepository())..add(SearchInitEvent(model: data)), - child: SearchPageContianer(), + return MultiProvider( + providers: [ + ChangeNotifierProvider.value(value: SearchTagNotifier()) + ], + child: MultiProvider( + providers: [ + + /// 搜索页面的bloc + BlocProvider( + create: (_) => SearchBloc(repository: SearchRepository())..add(SearchInitEvent(model: data)), + ), + + /// 输入框联想的bloc + BlocProvider( + create: (_)=> SearchThinkBloc(SearchThinkRepository()), + ), + + ], + child: SearchPageContianer(), + ), + // child: BlocProvider( + // create: (_) => SearchBloc(repository: SearchRepository())..add(SearchInitEvent(model: data)), + // child: SearchPageContianer(), + // ), ); } } @@ -30,17 +55,15 @@ class SearchPageContianer extends StatefulWidget { class _SearchPageContianerState extends State { /// tab轮播 TabController _tabController; - ScrollController _controller = ScrollController(); @override void initState() { - _tabController = TabController(length:0, vsync: ScrollableState()); + _tabController = TabController(length: 6, vsync: ScrollableState()); super.initState(); } @override void dispose() { - _controller?.dispose(); _tabController?.dispose(); super.dispose(); } @@ -94,44 +117,64 @@ class _SearchPageContianerState extends State { Widget _getMainWidget(List> datas) { return Scaffold( backgroundColor: Colors.white, - body: CustomScrollView( - controller: _controller, - slivers: _createContent(context, datas ?? []), + body: Column( + children: _createContentWidget(datas), ), ); } - List _createContent(BuildContext context, List> datas) { - List list = List(); - + List _createContentWidget(List> datas) { + List list = []; int length = datas?.length ?? 0; - if (length <= 0) { - list.add(SliverToBoxAdapter( - child: Container( - height: 200, - child: Center( - child: Text('暂时无数据哦~'), - ), + /// 骨架屏幕? + list.add(Container( + height: 200, + child: Center( + child: Text('暂时无数据哦~'), ), )); - return list; - } - - for (int i = 0; i < 3; i++) { - WidgetModel item = WidgetModel.fromJson(Map.from(datas[i])); - print('item.modName ${item.modName}'); - list.addAll(WidgetFactory.create( - item.modName, - isSliver: true, - model: datas[i], - )); + } else { + for (int i = 0; i < datas.length; i++) { + WidgetModel item = WidgetModel.fromJson(Map.from(datas[i])); + print('item.modName ${item.modName}'); + list.addAll(WidgetFactory.create( + item.modName, + isSliver: false, + model: datas[i], + )); + } } - - list.add(SliverFillRemaining( - child: Text('etstssss et'), - )); - return list; } + + // List _createContent(BuildContext context, List> datas) { + // List list = List(); + // + // int length = datas?.length ?? 0; + // + // if (length <= 0) { + // list.add(SliverToBoxAdapter( + // child: Container( + // height: 200, + // child: Center( + // child: Text('暂时无数据哦~'), + // ), + // ), + // )); + // return list; + // } + // + // for (int i = 0; i < 3; i++) { + // WidgetModel item = WidgetModel.fromJson(Map.from(datas[i])); + // print('item.modName ${item.modName}'); + // list.addAll(WidgetFactory.create( + // item.modName, + // isSliver: true, + // model: datas[i], + // )); + // } + // + // return list; + // } } diff --git a/lib/pages/search_page/search_page2.dart b/lib/pages/search_page/search_page2.dart deleted file mode 100644 index e6dd099..0000000 --- a/lib/pages/search_page/search_page2.dart +++ /dev/null @@ -1,183 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'bloc/search_bloc.dart'; -import 'bloc/search_repository.dart'; -import 'package:zhiying_comm/zhiying_comm.dart'; - -class SearchPage2 extends StatelessWidget { - final Map data; - - const SearchPage2(this.data); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) => SearchBloc(repository: SearchRepository())..add(SearchInitEvent(model: data)), - child: SearchPage2Container(), - ); - } -} - -class SearchPage2Container extends StatefulWidget { - @override - _SearchPage2ContainerState createState() => _SearchPage2ContainerState(); -} - -class _SearchPage2ContainerState extends State { - - TabController _controller; - - @override - void initState() { - _controller = TabController(length: tabTitle.length, vsync: ScrollableState()); - super.initState(); - } - - @override - void dispose() { - _controller?.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return MediaQuery.removePadding( - removeTop: true, - context: context, - child: Container( - width: double.infinity, - child: BlocConsumer( - listener: (BuildContext context, SearchState state) { - if (state is SearchErrorState) { - print('数据加载出错'); - } - }, - buildWhen: (previous, current) { - /// 数据加载出错不进行build - if (current is SearchErrorState) { - return false; - } - - /// 搜索成功,跳转结果页面 - if (current is SearchSubmitSuccessState) { - return false; - } - - /// 搜索失败,不进行build - if (current is SearchSubmitErrorState) { - return false; - } - - return true; - }, - builder: (context, state) { - print('currente state = $state'); - if (state is SearchLoadedState) { - return _getMainWidget(state?.model); - } - - /// 骨架屏幕 - return _getMainWidget(null); - }, - ), - ), - ); - } - - /// 主视图 - Widget _getMainWidget(List> datas){ - return Scaffold( - backgroundColor: Colors.white, - body: NestedScrollView( - headerSliverBuilder: (context, bool){ - return _createHeadWidget(context, datas); - }, - body: ListView( - children: [ - Text('sss') - ], - ), - ), - ); - } - - - /// 头部视图 - List _createHeadWidget(BuildContext context, List> datas){ - List list = []; - - int length = datas?.length?? 0; - if (length <= 0) { - list.add(SliverToBoxAdapter( - child: Container( - height: 200, - child: Center( - child: Text('暂时无数据哦~'), - ), - ), - )); - return list; - } - - for (int i = 0; i < 2; i++) { - WidgetModel item = WidgetModel.fromJson(Map.from(datas[i])); - print('item.modName ${item.modName}'); - list.addAll(WidgetFactory.create( - item.modName, - isSliver: true, - model: datas[i], - )); - } - list.add(_getTabbar()); - return list; - - } - - var tabTitle = [ - '页面1', - '页面2', - '页面3', - ]; - - Widget _getTabbar(){ - /// 悬停TabBar - return SliverPersistentHeader( - delegate: new SliverTabBarDelegate( - TabBar( - controller: _controller, - tabs: tabTitle.map((f) => Tab(text: f)).toList(), - indicatorColor: Colors.red, - unselectedLabelColor: Colors.black, - labelColor: Colors.red, - ), - ), - pinned: true, - ); - } - -} - -class SliverTabBarDelegate extends SliverPersistentHeaderDelegate { - final TabBar widget; - - - const SliverTabBarDelegate(this.widget) - : assert(widget != null); - - @override - Widget build( - BuildContext context, double shrinkOffset, bool overlapsContent) { - return Container( color:Colors.white,child: this.widget); - } - - @override - bool shouldRebuild(SliverTabBarDelegate oldDelegate) { - return false; - } - - @override - double get maxExtent => widget.preferredSize.height; - - @override - double get minExtent => widget.preferredSize.height; -} diff --git a/lib/pages/search_page/search_page3.dart b/lib/pages/search_page/search_page3.dart deleted file mode 100644 index 2087325..0000000 --- a/lib/pages/search_page/search_page3.dart +++ /dev/null @@ -1,148 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'bloc/search_bloc.dart'; -import 'bloc/search_repository.dart'; -import 'package:zhiying_comm/zhiying_comm.dart'; - -class SearchPage3 extends StatelessWidget { - final Map data; - - const SearchPage3(this.data); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) => SearchBloc(repository: SearchRepository())..add(SearchInitEvent(model: data)), - child: SearchPage3Container(), - ); - } -} - -class SearchPage3Container extends StatefulWidget { - @override - _SearchPage3ContainerState createState() => _SearchPage3ContainerState(); -} - -class _SearchPage3ContainerState extends State { - @override - Widget build(BuildContext context) { - return MediaQuery.removePadding( - removeTop: true, - context: context, - child: Container( - width: double.infinity, - child: BlocConsumer( - listener: (BuildContext context, SearchState state) { - if (state is SearchErrorState) { - print('数据加载出错'); - } - }, - buildWhen: (previous, current) { - /// 数据加载出错不进行build - if (current is SearchErrorState) { - return false; - } - - /// 搜索成功,跳转结果页面 - if (current is SearchSubmitSuccessState) { - return false; - } - - /// 搜索失败,不进行build - if (current is SearchSubmitErrorState) { - return false; - } - - return true; - }, - builder: (context, state) { - print('currente state = $state'); - if (state is SearchLoadedState) { - return _getMainWidget(state?.model); - } - - /// 骨架屏幕 - return _getMainWidget(null); - }, - ), - ), - ); - } - - /// 主视图 - Widget _getMainWidget(List> datas){ - return Scaffold( - backgroundColor: Colors.white, - body: NestedScrollView( - headerSliverBuilder: (context, bool){ - return _createHeadWidget(context, datas); - }, - body: Container( - child: Text('testssfee'), - ), - ), - ); - } - - - /// 头部视图 - List _createHeadWidget(BuildContext context, List> datas){ - List list = []; - - int length = datas?.length?? 0; - if (length <= 0) { - list.add(SliverToBoxAdapter( - child: Container( - height: 200, - child: Center( - child: Text('暂时无数据哦~'), - ), - ), - )); - return list; - } - - for (int i = 0; i < 3; i++) { - WidgetModel item = WidgetModel.fromJson(Map.from(datas[i])); - print('item.modName ${item.modName}'); - list.addAll(WidgetFactory.create( - item.modName, - isSliver: true, - model: datas[i], - )); - } - return list; - - } - - - /// 子视图 - - - List _createContent(BuildContext context, List> datas) { - List list = List(); - - /// datas.length - 1 为最后一个在底部 - for (int i = 0; i < datas.length; i++) { - WidgetModel item = WidgetModel.fromJson(Map.from(datas[i])); - print('item.modName ${item.modName}'); - list.addAll(WidgetFactory.create( - item.modName, - isSliver: true, - model: datas[i], - )); - } - if (list.length <= 0) { - list.add(SliverToBoxAdapter( - child: Container( - height: 200, - child: Center( - child: Text('暂时无数据哦~'), - ), - ), - )); - } - return list; - } -} - diff --git a/lib/pages/search_result_page/bloc/search_result_bloc.dart b/lib/pages/search_result_page/bloc/search_result_bloc.dart new file mode 100644 index 0000000..15b68c0 --- /dev/null +++ b/lib/pages/search_result_page/bloc/search_result_bloc.dart @@ -0,0 +1,46 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:zhiying_base_widget/pages/search_result_page/bloc/search_result_repository.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +part 'search_result_event.dart'; + +part 'search_result_state.dart'; + +class SearchResultBloc extends Bloc { + // SearchResultBloc() : super(SearchResultInitial()); + + SearchResultRepository repository; + + SearchResultBloc({@required this.repository}); + + @override + SearchResultState get initialState => SearchResultInitial(); + + @override + Stream mapEventToState( + SearchResultEvent event, + ) async* { + if (event is SearchResultInitEvent) { + yield* _mapInitEventToState(event); + } + } + + /// 初始化 + Stream _mapInitEventToState(SearchResultInitEvent event) async* { + var cache = await repository.fetchCachedData(event.model); + if(!EmptyUtil.isEmpty(cache)){ + yield SearchResultLoadedState(model: cache); + } + var result = await repository.fetchInit(event.model); + if(!EmptyUtil.isEmpty(result)){ + yield SearchResultLoadedState(model: result); + }else{ + yield SearchResultErrorState(); + } + + } +} diff --git a/lib/pages/search_result_page/bloc/search_result_event.dart b/lib/pages/search_result_page/bloc/search_result_event.dart new file mode 100644 index 0000000..76bf5f8 --- /dev/null +++ b/lib/pages/search_result_page/bloc/search_result_event.dart @@ -0,0 +1,18 @@ +part of 'search_result_bloc.dart'; + +abstract class SearchResultEvent extends Equatable { + const SearchResultEvent(); + + @override + List get props => []; +} + +/// 初始化数据 +class SearchResultInitEvent extends SearchResultEvent { + final Map model; + + const SearchResultInitEvent({@required this.model}); + + @override + List get props => [this.model]; +} diff --git a/lib/pages/search_result_page/bloc/search_result_repository.dart b/lib/pages/search_result_page/bloc/search_result_repository.dart new file mode 100644 index 0000000..6bb3002 --- /dev/null +++ b/lib/pages/search_result_page/bloc/search_result_repository.dart @@ -0,0 +1,60 @@ +import 'dart:convert'; + +import 'package:zhiying_comm/zhiying_comm.dart'; + +class SearchResultRepository { + List> _pageData = []; + + /// 获取网络数据 + Future>> fetchInit(final Map model) async { + String keywords = model.containsKey('keywords') ? model['keywords'] : ''; + String type = model.containsKey('type') ? model['type'] : 'taobao'; + var result = await NetUtil.post('/api/v1/mod/pub.flutter.search_result', method: NetMethod.GET, cache: true); + if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result)) { + try { + List> modList = List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['mod_list']); + modList.forEach((Map item) { + var data = jsonDecode(item['data']); + data['keywords'] = keywords; + data['type'] = type; + item['data'] = jsonEncode(data); + _pageData.add(item); + }); + + return _pageData; + } catch (e) { + Logger.error(e.toString()); + } + } + return null; + } + + /// 获取缓存数据 + Future>> fetchCachedData(final Map model) async { + String keywords = model.containsKey('keywords') ? model['keywords'] : ''; + String type = model.containsKey('type') ? model['type'] : 'taobao'; + var result = await NetUtil.getRequestCachedData('/api/v1/mod/pub.flutter.search_result'); + try { + if (!EmptyUtil.isEmpty(result)) { + // return List.from(result['mod_list']); + List> modList = List.from(result['mod_list']); + List> _pageData = []; + + modList.forEach((Map item) { + var data = jsonDecode(item['data']); + data['keywords'] = keywords; + data['type'] = type; + item['data'] = jsonEncode(data); + _pageData.add(item); + }); + return _pageData; + } + } catch (e) {} + return null; + } + + /// 搜索的方法 + Future fetchSearchSubmit(final String model) async { + var result = await NetUtil.post(''); + } +} diff --git a/lib/pages/search_result_page/bloc/search_result_state.dart b/lib/pages/search_result_page/bloc/search_result_state.dart new file mode 100644 index 0000000..0b962a7 --- /dev/null +++ b/lib/pages/search_result_page/bloc/search_result_state.dart @@ -0,0 +1,26 @@ +part of 'search_result_bloc.dart'; + +abstract class SearchResultState extends Equatable { + const SearchResultState(); + + @override + List get props => []; +} + +class SearchResultInitial extends SearchResultState { + @override + List get props => []; +} + +/// 数据加载完毕 +class SearchResultLoadedState extends SearchResultState { + List> model; + + SearchResultLoadedState({@required this.model}); + + @override + List get props => [this.model]; +} + +/// 数据加载失败 +class SearchResultErrorState extends SearchResultState {} diff --git a/lib/pages/search_result_page/item/bloc/search_result_item_bloc.dart b/lib/pages/search_result_page/item/bloc/search_result_item_bloc.dart new file mode 100644 index 0000000..a867af3 --- /dev/null +++ b/lib/pages/search_result_page/item/bloc/search_result_item_bloc.dart @@ -0,0 +1,48 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:zhiying_base_widget/pages/search_result_page/item/bloc/search_result_item_repository.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +part 'search_result_item_event.dart'; + +part 'search_result_item_state.dart'; + +class SearchResultItemBloc extends Bloc { + // SearchResultItemBloc() : super(SearchResultItemInitial()); + + SearchResultItemRepository repository; + + @override + SearchResultItemState get initialState => SearchResultItemInitial(); + + SearchResultItemBloc({@required this.repository}); + + @override + Stream mapEventToState( + SearchResultItemEvent event, + ) async* { + if (event is SearchResultItemInitEvent) { + yield* _mapInitEventToState(event); + } + } + + /// 初始化 + Stream _mapInitEventToState(SearchResultItemInitEvent event) async* { + /// 获取缓存数据 + var cache = await repository.fetchCacheData(event.model); + if (!EmptyUtil.isEmpty(cache)) { + yield SearchResultItemLoadedState(model: cache); + } + + /// 访问网络数据 + var result = await repository.fetchInitData(event.model); + if (!EmptyUtil.isEmpty(result)) { + yield SearchResultItemLoadedState(model: result); + } else { + yield SearchResultItemErrorState(); + } + } +} diff --git a/lib/pages/search_result_page/item/bloc/search_result_item_event.dart b/lib/pages/search_result_page/item/bloc/search_result_item_event.dart new file mode 100644 index 0000000..16fb696 --- /dev/null +++ b/lib/pages/search_result_page/item/bloc/search_result_item_event.dart @@ -0,0 +1,15 @@ +part of 'search_result_item_bloc.dart'; + +abstract class SearchResultItemEvent extends Equatable { + const SearchResultItemEvent(); +} + +/// 初始化 +class SearchResultItemInitEvent extends SearchResultItemEvent{ + + final Map model; + const SearchResultItemInitEvent({@required this.model}); + + @override + List get props => [this.model]; +} diff --git a/lib/pages/search_result_page/item/bloc/search_result_item_repository.dart b/lib/pages/search_result_page/item/bloc/search_result_item_repository.dart new file mode 100644 index 0000000..81156bb --- /dev/null +++ b/lib/pages/search_result_page/item/bloc/search_result_item_repository.dart @@ -0,0 +1,55 @@ +import 'dart:convert'; + +import 'package:zhiying_comm/zhiying_comm.dart'; + +class SearchResultItemRepository { + List> _pageData; + + /// 初始化 + Future>> fetchInitData(final Map model) async { + String type = model['type']; + String keywords = model.containsKey('keywords') ? model['keywords'] : ''; + if (EmptyUtil.isEmpty(type)) return null; + var result = await NetUtil.post('/api/v1/mod/pub.flutter.search_result.$type', method: NetMethod.GET, cache: true); + if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result)) { + try { + List> modList = List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['mod_list']); + + modList.forEach((Map item) { + var data = jsonDecode(item['data']); + data['keywords'] = keywords; + item['data'] = jsonEncode(data); + _pageData.add(item); + }); + + return _pageData; + } catch (e) { + Logger.error(e.toString()); + } + } + return null; + } + + /// 获取缓存数据 + Future>> fetchCacheData(final Map model) async { + String type = model['type'] ?? ''; + String keywords = model.containsKey('keywords') ? model['keywords'] : ''; + if (EmptyUtil.isEmpty(type)) return null; + var result = await NetUtil.getRequestCachedData('/api/v1/mod/pub.flutter.search_result.$type'); + try { + if (!EmptyUtil.isEmpty(result)) { + List> modList = List.from(result['mod_list']); + List> _pageData = []; + + modList.forEach((Map item) { + var data = jsonDecode(item['data']); + data['keywords'] = keywords; + item['data'] = jsonEncode(data); + _pageData.add(item); + }); + return _pageData; + } + } catch (e) {} + return null; + } +} diff --git a/lib/pages/search_result_page/item/bloc/search_result_item_state.dart b/lib/pages/search_result_page/item/bloc/search_result_item_state.dart new file mode 100644 index 0000000..55ec28e --- /dev/null +++ b/lib/pages/search_result_page/item/bloc/search_result_item_state.dart @@ -0,0 +1,23 @@ +part of 'search_result_item_bloc.dart'; + +abstract class SearchResultItemState extends Equatable { + const SearchResultItemState(); + + @override + List get props => []; +} + +class SearchResultItemInitial extends SearchResultItemState {} + +/// 数据加载完毕 +class SearchResultItemLoadedState extends SearchResultItemState { + List> model; + + SearchResultItemLoadedState({@required this.model}); + + @override + List get props => [this.model]; +} + +/// 数据加载失败 +class SearchResultItemErrorState extends SearchResultItemState {} diff --git a/lib/pages/search_result_page/item/notifier/search_result_item_page_notifier.dart b/lib/pages/search_result_page/item/notifier/search_result_item_page_notifier.dart new file mode 100644 index 0000000..fc9ce8e --- /dev/null +++ b/lib/pages/search_result_page/item/notifier/search_result_item_page_notifier.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +class SearchResultItemPageNotifier with ChangeNotifier { + bool scrollEnd = false; + + // 加载更多数据 + void loadMore() { + scrollEnd = true; + notifyListeners(); + } + + void reset() { + scrollEnd = false; + notifyListeners(); + } +} diff --git a/lib/pages/search_result_page/item/search_result_item_page.dart b/lib/pages/search_result_page/item/search_result_item_page.dart new file mode 100644 index 0000000..981a6cc --- /dev/null +++ b/lib/pages/search_result_page/item/search_result_item_page.dart @@ -0,0 +1,195 @@ +import 'package:flutter/material.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:zhiying_base_widget/pages/search_result_page/item/bloc/search_result_item_bloc.dart'; +import 'package:zhiying_base_widget/pages/search_result_page/item/bloc/search_result_item_repository.dart'; +import 'package:zhiying_base_widget/widgets/home/home_goods/skeleton/home_goods_sk.dart'; +import 'package:zhiying_base_widget/widgets/search_result/goods_list/bloc/search_result_goods_list_bloc.dart'; +import 'package:zhiying_base_widget/widgets/search_result/goods_list/bloc/search_result_goods_list_repository.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:provider/provider.dart'; + +import 'notifier/search_result_item_page_notifier.dart'; + +class SearchResultItemPage extends StatelessWidget { + final Map data; + + const SearchResultItemPage(this.data); + + @override + Widget build(BuildContext context) { + return MultiProvider( + providers: [ + ChangeNotifierProvider.value(value: SearchResultItemPageNotifier()), + ], + // child: BlocProvider( + // create: (_) => SearchResultItemBloc(repository: SearchResultItemRepository())..add(SearchResultItemInitEvent(model: data)), + // child: SearchResultItemPageContianer(), + // ), + child: MultiBlocProvider( + providers: [ + /// 视图BLOC + BlocProvider( + create: (_) => SearchResultItemBloc(repository: SearchResultItemRepository())..add(SearchResultItemInitEvent(model: data)), + ), + + /// 商品列表Bloc + BlocProvider( + create: (_) => SearchResultGoodsListBloc(repository: SearchResultGoodsListRepository(reqData: data)), + ), + ], + child: SearchResultItemPageContianer(), + ), + ); + } +} + +class SearchResultItemPageContianer extends StatefulWidget { + @override + _SearchResultItemPageContianerState createState() => _SearchResultItemPageContianerState(); +} + +class _SearchResultItemPageContianerState extends State with AutomaticKeepAliveClientMixin { + @override + bool get wantKeepAlive => true; + + RefreshController _refreshController; + ScrollController _controller; + bool _isEnded = false; + + @override + void initState() { + _refreshController = RefreshController(); + _controller = ScrollController(); + + _controller.addListener(() { + // print('${_controller.offset} ${_controller.position.maxScrollExtent}'); + if (_controller.offset >= _controller.position.maxScrollExtent && !_isEnded) { + // 滑动到底部 + _isEnded = true; + Provider.of(context, listen: false).loadMore(); + } else if (_controller.offset < _controller.position.maxScrollExtent && _isEnded) { + _isEnded = false; + Provider.of(context, listen: false).reset(); + } + }); + + super.initState(); + } + + @override + void dispose() { + _refreshController?.dispose(); + _controller?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocConsumer( + listener: (BuildContext context, SearchResultItemState state) { + if (state is SearchResultItemErrorState) { + print('数据加载出错'); + } + }, + buildWhen: (previous, current) { + /// 数据加载出错不进行build + if (current is SearchResultItemErrorState) { + return false; + } + return true; + }, + builder: (context, state) { + print('currente state = $state'); + if (state is SearchResultItemLoadedState) { + return _getMainWidget(state?.model ?? null); + } + + /// 骨架屏幕 + return _getMainWidget(null); + }, + ); + } + + /// 上拉更多 + void _onLoading() async { + BlocProvider.of(context).add(SearchResultGoodsListOnLoadEvent()); + _refreshController.loadComplete(); + } + + /// 下拉刷新 + void _onRefresh() async { + BlocProvider.of(context).add(SearchResultGoodsListOnRefreshEvent()); + _refreshController.refreshCompleted(resetFooterState: true); + } + + /// 获取主视图 + Widget _getMainWidget(List> datas) { + return Scaffold( + backgroundColor: HexColor.fromHex('#F9F9F9'), + body: Stack( + children: [ + Positioned( + top: 20, + left: 0, + right: 0, + bottom: 0, + child: SmartRefresher( + onLoading: _onLoading, + onRefresh: _onRefresh, + enablePullDown: true, + enablePullUp: true, + // footer: ClassicFooter(), + // header: WaterDropHeader(), + controller: _refreshController, + child: CustomScrollView( + controller: _controller, + slivers: _createContentWidget(datas), + ), + ), + ), + + /// 筛选栏 + _getSearchResultSortWidget(datas), + ], + ), + ); + } + + /// 筛选栏 + Widget _getSearchResultSortWidget(List> datas) { + int length = datas?.length ?? 0; + if (length == 0) { + return Container(); + } else { + WidgetModel item = WidgetModel.fromJson(Map.from(datas[0])); + List itemWidgets = WidgetFactory.create(item.modName, isSliver: false, model: datas[0]); + int length = itemWidgets?.length ?? 0; + return Align(alignment: Alignment.topCenter, child: length > 0 ? itemWidgets[0] : Container()); + } + } + + /// 创建Widget + List _createContentWidget(List> datas) { + List list = []; + int length = datas?.length ?? 0; + if (length > 0) { + /// 去除筛选栏, 从1开始 + for (int i = 1; i < datas.length; i++) { + WidgetModel item = WidgetModel.fromJson(Map.from(datas[i])); + print('item.modName ${item.modName}'); + list.addAll(WidgetFactory.create( + item.modName, + isSliver: true, + model: datas[i], + )); + } + } else { + list.add(SliverToBoxAdapter( + /// TODO 改成骨架图 + child: HomeGoodsSkeleton(), + )); + } + return list; + } +} diff --git a/lib/pages/search_result_page/search_result_page.dart b/lib/pages/search_result_page/search_result_page.dart new file mode 100644 index 0000000..99f3b14 --- /dev/null +++ b/lib/pages/search_result_page/search_result_page.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/pages/search_result_page/bloc/search_result_bloc.dart'; +import 'package:zhiying_base_widget/pages/search_think_page/bloc/search_think_bloc.dart'; +import 'package:zhiying_base_widget/pages/search_think_page/bloc/search_think_repository.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'bloc/search_result_repository.dart'; + +/// +/// 搜索结果页面 +/// +class SearchResultPage extends StatelessWidget { + final Map data; + + const SearchResultPage(this.data); + + @override + Widget build(BuildContext context) { + // return BlocProvider( + // create: (_) => SearchResultBloc(repository: SearchResultRepository())..add(SearchResultInitEvent(model: data)), + // child: SearchResultContianer(), + // ); + + return MultiBlocProvider( + providers: [ + /// 页面的数据 + BlocProvider( + create: (_) => SearchResultBloc(repository: SearchResultRepository())..add(SearchResultInitEvent(model: data)), + ), + + /// 输入框联想的bloc + BlocProvider( + create: (_)=> SearchThinkBloc(SearchThinkRepository()), + ), + ], + child: SearchResultContianer(), + ); + } +} + +class SearchResultContianer extends StatefulWidget { + @override + _SearchResultContianerState createState() => _SearchResultContianerState(); +} + +class _SearchResultContianerState extends State { + @override + Widget build(BuildContext context) { + return BlocConsumer( + listener: (BuildContext context, SearchResultState state) { + if (state is SearchResultErrorState) { + print('数据加载出错'); + } + }, + buildWhen: (previous, current) { + /// 数据加载出错不进行build + if (current is SearchResultErrorState) { + return false; + } + + return true; + }, + builder: (context, state) { + print('currente state = $state'); + if (state is SearchResultLoadedState) { + return _getMainWidget(state?.model); + } + + /// 骨架屏幕 + return _getMainWidget(null); + }, + ); + } + + /// 获取主视图 + Widget _getMainWidget(List> data) { + return Scaffold( + backgroundColor: Colors.white, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: _createContentWidget(data), + ), + ); + } + + /// 创建视图 + List _createContentWidget(List> datas) { + int length = datas?.length ?? 0; + List list = []; + + if (length > 0) { + for (int i = 0; i < length; i++) { + WidgetModel item = WidgetModel.fromJson(Map.from(datas[i])); + print('item.modName ${item.modName}'); + list.addAll(WidgetFactory.create( + item.modName, + isSliver: false, + model: datas[i], + )); + } + } else { + list.add(Container()); + } + + return list; + } +} diff --git a/lib/pages/search_think_page/bloc/search_think_bloc.dart b/lib/pages/search_think_page/bloc/search_think_bloc.dart new file mode 100644 index 0000000..80eb345 --- /dev/null +++ b/lib/pages/search_think_page/bloc/search_think_bloc.dart @@ -0,0 +1,61 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:zhiying_base_widget/pages/search_think_page/bloc/search_think_repository.dart'; +import 'package:zhiying_base_widget/pages/search_think_page/model/search_think_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +part 'search_think_event.dart'; + +part 'search_think_state.dart'; + +class SearchThinkBloc extends Bloc { + // SearchThinkBloc() : super(SearchThinkInitial()); + @override + SearchThinkState get initialState => SearchThinkInitial(); + + SearchThinkRepository repository; + + SearchThinkBloc(this.repository); + + @override + Stream mapEventToState(SearchThinkEvent event,) async* { + + /// 关键字改变的方法 + if (event is SearchThinkKeyWrodsChangeEvent) { + yield* _mapKeyWordsChangeToState(event); + } + + /// 修改渠道关键字 + if (event is SearchThinkChangeTypeEvent) { + _mapChangeTypeToState(event); + } + + /// 显示原本的视图 + if (event is SearchThinkShowBaseViewEvent) { + yield* _mapShowBaseViewEventToState(); + } + } + + /// 关键字改变的方法 + Stream _mapKeyWordsChangeToState(SearchThinkKeyWrodsChangeEvent event) async* { + var result = await repository.fetchkeyWordsChange(event.keywords); + if (!EmptyUtil.isEmpty(result)) { + yield SearchThinkLoadedState(result); + } else { + yield SearchThinkErrorState(); + } + } + + /// 显示原本的视图 + Stream _mapShowBaseViewEventToState() async* { + yield SearchThinkShowBaseViewStatte(); + } + + /// 修改商品电商类型 + _mapChangeTypeToState(SearchThinkChangeTypeEvent event) { + repository.changeType(event.type); + } +} diff --git a/lib/pages/search_think_page/bloc/search_think_event.dart b/lib/pages/search_think_page/bloc/search_think_event.dart new file mode 100644 index 0000000..f7930f1 --- /dev/null +++ b/lib/pages/search_think_page/bloc/search_think_event.dart @@ -0,0 +1,31 @@ +part of 'search_think_bloc.dart'; + +abstract class SearchThinkEvent extends Equatable { + const SearchThinkEvent(); +} + +/// 数据加载 +class SearchThinkKeyWrodsChangeEvent extends SearchThinkEvent { + final String keywords; + + const SearchThinkKeyWrodsChangeEvent(this.keywords, ); + + @override + List get props => [this.keywords]; +} + +/// 修改电商类型 +class SearchThinkChangeTypeEvent extends SearchThinkEvent{ + final String type; + + SearchThinkChangeTypeEvent(this.type); + + @override + List get props => [this.props]; +} + +/// 显示原本视图 +class SearchThinkShowBaseViewEvent extends SearchThinkEvent{ + @override + List get props => []; +} \ No newline at end of file diff --git a/lib/pages/search_think_page/bloc/search_think_repository.dart b/lib/pages/search_think_page/bloc/search_think_repository.dart new file mode 100644 index 0000000..67668cc --- /dev/null +++ b/lib/pages/search_think_page/bloc/search_think_repository.dart @@ -0,0 +1,35 @@ +import 'package:zhiying_base_widget/pages/search_think_page/model/search_think_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +class SearchThinkRepository { + + + // 商品电商渠道类型,默认为淘宝 + String type = 'taobao'; + + /// 关键字改变 + Future> fetchkeyWordsChange(String keywrods) async { + if (!EmptyUtil.isEmpty(keywrods)) { + var result = await NetUtil.post('/api/v1/s/t/$keywrods', method: NetMethod.GET); + try { + if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { + List data = List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); + List newDatas = []; + data.forEach((item){ + newDatas.add(SearchThinkModel(type: type, keywords: item)); + }); + return newDatas; + } + } catch (e) { + Logger.error(e.toString()); + } + } + return null; + } + + /// 修改渠道类型 + void changeType(String newType){ + this.type = newType; + print('change type = $type'); + } +} diff --git a/lib/pages/search_think_page/bloc/search_think_state.dart b/lib/pages/search_think_page/bloc/search_think_state.dart new file mode 100644 index 0000000..a8b6104 --- /dev/null +++ b/lib/pages/search_think_page/bloc/search_think_state.dart @@ -0,0 +1,30 @@ +part of 'search_think_bloc.dart'; + +abstract class SearchThinkState extends Equatable { + const SearchThinkState(); +} + +class SearchThinkInitial extends SearchThinkState { + @override + List get props => []; +} + +/// 数据加载完毕 +class SearchThinkLoadedState extends SearchThinkState{ + List model; + SearchThinkLoadedState(this.model); + @override + List get props => [this.model]; +} + +/// 显示本来的视图 +class SearchThinkShowBaseViewStatte extends SearchThinkState{ + @override + List get props => []; +} + +/// 数据加载出错 +class SearchThinkErrorState extends SearchThinkState{ + @override + List get props => []; +} diff --git a/lib/pages/search_think_page/model/search_think_model.dart b/lib/pages/search_think_page/model/search_think_model.dart new file mode 100644 index 0000000..11b49dc --- /dev/null +++ b/lib/pages/search_think_page/model/search_think_model.dart @@ -0,0 +1,20 @@ +class SearchThinkModel { + String type; + String keywords; + + SearchThinkModel({this.type, this.keywords}); + + factory SearchThinkModel.fromJson(Map json) { + return SearchThinkModel( + type: json['type'], + keywords: json['keywords'], + ); + } + + Map toJson() { + final Map data = new Map(); + data['type'] = this.type; + data['keywords'] = this.keywords; + return data; + } +} diff --git a/lib/pages/search_think_page/search_think_page.dart b/lib/pages/search_think_page/search_think_page.dart new file mode 100644 index 0000000..d44fcbc --- /dev/null +++ b/lib/pages/search_think_page/search_think_page.dart @@ -0,0 +1,147 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'dart:ui'; + +/// +/// 搜索联想页面 +/// +class SearchThinkPage extends StatefulWidget { + String keywords; + final Map data; + + SearchThinkPage(this.data, {Key key}) : super(key: key) { + try {} catch (_) {} + } + + @override + _SearchThinkPageState createState() => _SearchThinkPageState(); +} + +class _SearchThinkPageState extends State { + TextEditingController _textEditingController; + FocusNode _focusNode; + TabController _tabController; + + /// 打开搜索结果 + void _openSearchResult(String keywords) {} + + /// 关闭当前页面 + void _closePage() { + Navigator.maybePop(context); + } + + /// 点击联想结果页面 + void _onClickThinkResult(String keywords) {} + + @override + void initState() { + _textEditingController = TextEditingController(text: widget?.keywords); + _focusNode = FocusNode(); + super.initState(); + } + + @override + void dispose() { + _textEditingController?.dispose(); + _focusNode?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Container( + margin: EdgeInsets.only(top: MediaQueryData.fromWindow(window).padding.top + 7.5), + color: HexColor.fromHex('#FFFFFF'), + width: double.infinity, + child: Column( + children: [ + /// 标题 + _buildTitleWidget(), + + /// 联想结果 + Expanded( + child: _buildThinkWidget(null), + ) + ], + ), + ), + ); + } + + /// 联想结果 + Widget _buildThinkWidget(List datas) { + int length = datas?.length ?? 0; + return Visibility( + visible: length > 0, + child: ListView.builder( + itemBuilder: (context, item) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 13), + child: Text('', style: TextStyle(color: HexColor.fromHex('#333333'), fontSize: 14)), + ); + }, + itemCount: length, + ), + ); + } + + /// titile + Widget _buildTitleWidget() { + return Container( + height: 32, + width: double.infinity, + child: Row( + children: [ + /// 输入框 + Expanded( + child: _buildInputWidget(), + ), + + /// 取消按钮 + _buildButtomWidget(), + ], + ), + ); + } + + /// 取消按钮 + Widget _buildButtomWidget() { + return GestureDetector( + onTap: () => _closePage(), + child: Container( + padding: const EdgeInsets.only(right: 12.5, left: 10, bottom: 6, top: 6), + child: Text('取消', style: TextStyle(fontSize: 14, color: HexColor.fromHex('#999999'))), + ), + ); + } + + /// 输入框 + Widget _buildInputWidget() { + return Container( + height: 32, + margin: const EdgeInsets.only(left: 12.5), + decoration: BoxDecoration( + color: HexColor.fromHex('#F9F9F9'), + borderRadius: BorderRadius.circular(30), + ), + child: TextField( + controller: _textEditingController, + focusNode: _focusNode, + style: TextStyle(color: HexColor.fromHex('#333333'), fontSize: 13, textBaseline: TextBaseline.alphabetic), + decoration: InputDecoration( + contentPadding: EdgeInsets.only(left: 12.5, right: 12.5, bottom: 12), + fillColor: Colors.transparent, + filled: true, + border: InputBorder.none, + errorBorder: InputBorder.none, + enabledBorder: InputBorder.none, + disabledBorder: InputBorder.none, + focusedErrorBorder: InputBorder.none, + focusedBorder: InputBorder.none, + ), + ), + ); + } +} diff --git a/lib/register.dart b/lib/register.dart index 16390b2..42c1224 100644 --- a/lib/register.dart +++ b/lib/register.dart @@ -8,12 +8,15 @@ import 'package:zhiying_base_widget/pages/launch_page/launch_page.dart'; import 'package:zhiying_base_widget/pages/main_page/main_page.dart'; import 'package:zhiying_base_widget/pages/mine_detail_page/mine_detail_page.dart'; import 'package:zhiying_base_widget/pages/orders_page/orders_page.dart'; +import 'package:zhiying_base_widget/pages/search_page/item/search_item_page.dart'; +import 'package:zhiying_base_widget/pages/search_result_page/item/search_result_item_page.dart'; +import 'package:zhiying_base_widget/pages/search_think_page/search_think_page.dart'; import 'package:zhiying_base_widget/pages/security_page/security_bind_alipay/security_bind_alipay_page.dart'; import 'package:zhiying_base_widget/pages/security_page/security_mobile/security_mobile.dart'; import 'package:zhiying_base_widget/pages/security_page/security_page.dart'; import 'package:zhiying_base_widget/pages/security_page/security_password/security_password.dart'; import 'package:zhiying_base_widget/pages/setting_page/setting_page.dart'; -import 'package:zhiying_base_widget/pages/sreach_result_page/sreach_result_page.dart'; +import 'package:zhiying_base_widget/pages/search_result_page/search_result_page.dart'; import 'package:zhiying_base_widget/pages/wallet_page/wallet_page.dart'; import 'package:zhiying_base_widget/pages/webview/base_webview.dart'; import 'package:zhiying_base_widget/widgets/goods_details/footer/goods_details_footer_widget.dart'; @@ -33,6 +36,8 @@ import 'package:zhiying_base_widget/widgets/mine/mine_nav/mine_nav_bg.dart'; import 'package:zhiying_base_widget/widgets/mine/mine_nav/mine_nav_creater.dart'; import 'package:zhiying_base_widget/widgets/mine/mine_quick_entry/mine_quick_entry.dart'; import 'package:zhiying_base_widget/widgets/others/normal_nav/normal_nav_creater.dart'; +import 'package:zhiying_base_widget/widgets/search_result/sort/search_result_sort_widget.dart'; +import 'package:zhiying_base_widget/widgets/search_result/tarbar/search_result_tab_creater.dart'; import 'package:zhiying_base_widget/widgets/search/appbar/search_appbar_creater.dart'; import 'package:zhiying_base_widget/widgets/search/input/search_input_creater.dart'; import 'package:zhiying_base_widget/widgets/search/tabbar/search_tab_creater.dart'; @@ -51,6 +56,18 @@ import 'widgets/goods_details/evaluate/goods_details_evaluate_widget.dart'; import 'widgets/goods_details/recommend/goods_detail_commend_creater.dart'; import 'widgets/goods_details/title/goods_details_title_widget.dart'; import 'widgets/home/home_quick_entry/home_quick_entry.dart'; +import 'widgets/home/home_sreach/home_sreach_creater.dart'; +import 'widgets/search/appbar/search_appbar_creater.dart'; +import 'widgets/search/appbar/search_appbar_widget.dart'; +import 'widgets/search/history_tag/search_history_tag.dart'; +import 'widgets/search/hot_tag/search_hot_tag_widget.dart'; +import 'widgets/search/input/search_input_creater.dart'; +import 'widgets/search/input/search_input_widget.dart'; +import 'widgets/search/tabbar/search_tab_creater.dart'; +import 'widgets/search/tabbar/search_tab_widget.dart'; +import 'widgets/search_result/goods_list/search_result_goods_list_widget.dart'; +import 'widgets/search_result/search_input/search_result_input.dart'; + class BaseWidgetRegister { /// 初始化方法 @@ -86,8 +103,11 @@ class BaseWidgetRegister { PageFactory.regist( 'pub.flutter.my_wallet', (model) => BilDetailPage(model)); PageFactory.regist('goods_details', (model) => GoodsDetailsPage(model)); - PageFactory.regist('sreach', (model) => SearchPage(model)); - PageFactory.regist('sreach_result', (model) => SreachResultPage()); + PageFactory.regist('search', (model) => SearchPage(model)); + PageFactory.regist('search_item_page', (model) => SearchItemPage(model)); + PageFactory.regist('search_result', (model) => SearchResultPage(model)); + PageFactory.regist('search_result_item', (model) => SearchResultItemPage(model)); + // PageFactory.regist('search_think_page', (model) => SearchThinkPage(model)); // PageFactory.regist('login', (model) => LoginPage(model)); // PageFactory.regist('login_quick', (model) => LoginQuickPage(model)); // PageFactory.regist('login_account', (model) => LoginAccountPage(model)); @@ -135,12 +155,10 @@ class BaseWidgetRegister { WidgetFactory.regist('index_recommend_list', GoodsListCreater()); /// 首页快速入口 - WidgetFactory.regist( - 'multi_nav', DefaultWidgetCreater((model) => HomeQuickEntry(model))); + WidgetFactory.regist('multi_nav', DefaultWidgetCreater((model) => HomeQuickEntry(model))); /// 滚动公告 - WidgetFactory.regist('index_placard', - DefaultWidgetCreater((model) => HomeNoticeWidget(model))); + WidgetFactory.regist('index_placard', DefaultWidgetCreater((model) => HomeNoticeWidget(model))); /// 不可以滚动banner WidgetFactory.regist('index_banner_one', HomeBannerCreater()); @@ -150,64 +168,82 @@ class BaseWidgetRegister { /// ==================== 搜索页面 ==================== /// // 搜索标题 // WidgetFactory.regist('search_index_app_bar', DefaultWidgetCreater((model) => SearchAppbarWidget(model))); - WidgetFactory.regist('search_index_app_bar', SearchAppbarCreater()); // 搜索输入框 - WidgetFactory.regist('search_index_input', SearchInputCreater()); - // WidgetFactory.regist('search_index_input', DefaultWidgetCreater((model) => SearchInputWidget(model))); + WidgetFactory.regist('search_index_input', DefaultWidgetCreater((model) => SearchInputWidget(model))); // // 搜索tabBar WidgetFactory.regist('search_index_icon_list', SearcchTabCreater()); // WidgetFactory.regist('search_index_icon_list', DefaultWidgetCreater((model) => SearchTabWidget(model))); // // 热门搜索标签 - // WidgetFactory.regist('search_index', null); + WidgetFactory.regist('search_index_host_keyword', DefaultWidgetCreater((model) => SearchHotTagWidget(model))); // // 历史搜索标签 - // WidgetFactory.regist('search_index_history', null); + WidgetFactory.regist('search_index_history', DefaultWidgetCreater((model) => SearchHistoryTagWidget(model))); + + /// ==================== 搜索结果页面 ==================== /// + // 输入框 + WidgetFactory.regist('search_result_input', DefaultWidgetCreater((model) => SearchResultInputWidget(model))); + // tabbar + WidgetFactory.regist('search_result_icon_list', SearchResultTabCreater()); + + // 搜索结果页筛选widget 淘宝 + WidgetFactory.regist('search_result_taobao_sort', DefaultWidgetCreater((model) => SearchResultSortWidget(model))); + // 搜索结果的商品列表 淘宝 + WidgetFactory.regist('search_result_taobao_item', DefaultWidgetCreater((model) => SearchResultGoodsListWidget(model))); + + // 搜索结果页筛选widget 京东 + WidgetFactory.regist('search_result_jd_sort', DefaultWidgetCreater((model) => SearchResultSortWidget(model))); + // 搜索结果的商品列表 京东 + WidgetFactory.regist('search_result_jd_item', DefaultWidgetCreater((model) => SearchResultGoodsListWidget(model))); + + // 搜索结果页筛选widget 苏宁 + WidgetFactory.regist('search_result_suning_sort', DefaultWidgetCreater((model) => SearchResultSortWidget(model))); + // 搜索结果的商品列表 苏宁 + WidgetFactory.regist('search_result_suning_item', DefaultWidgetCreater((model) => SearchResultGoodsListWidget(model))); + + // 搜索结果页筛选widget 拼多多 + WidgetFactory.regist('search_result_pdd_sort', DefaultWidgetCreater((model) => SearchResultSortWidget(model))); + // 搜索结果的商品列表 拼多多 + WidgetFactory.regist('search_result_pdd_item', DefaultWidgetCreater((model) => SearchResultGoodsListWidget(model))); + + // 搜索结果页筛选widget 唯品会 + WidgetFactory.regist('search_result_vip', DefaultWidgetCreater((model) => SearchResultSortWidget(model))); + // 搜索结果的商品列表 唯品会 + WidgetFactory.regist('search_result_vip_item', DefaultWidgetCreater((model) => SearchResultGoodsListWidget(model))); + + // 搜索结果页筛选widget 考拉 + WidgetFactory.regist('search_result_kaola', DefaultWidgetCreater((model) => SearchResultSortWidget(model))); + // 搜索结果的商品列表 考拉 + WidgetFactory.regist('search_result_kaola_item', DefaultWidgetCreater((model) => SearchResultGoodsListWidget(model))); /// ==================== 商品详情 ==================== /// // 商品详情轮播图 - WidgetFactory.regist('product_detail_carousel', - DefaultWidgetCreater((model) => GoodsDetailsSlideBannerWidget(model))); + WidgetFactory.regist('product_detail_carousel', DefaultWidgetCreater((model) => GoodsDetailsSlideBannerWidget(model))); // 商品详情下载APP提示 - WidgetFactory.regist('product_detail_download_tips', - DefaultWidgetCreater((model) => UpgradeTipWidget(model))); + WidgetFactory.regist('product_detail_download_tips', DefaultWidgetCreater((model) => UpgradeTipWidget(model))); // 商品详情价格显示 - WidgetFactory.regist('product_detail_price', - DefaultWidgetCreater((model) => GoodsDetailsPriceWidget(model))); + WidgetFactory.regist('product_detail_price', DefaultWidgetCreater((model) => GoodsDetailsPriceWidget(model))); // 商品详情标题 - WidgetFactory.regist('product_detail_title', - DefaultWidgetCreater((model) => GoodsDetailsTitleWidget(model))); + WidgetFactory.regist('product_detail_title', DefaultWidgetCreater((model) => GoodsDetailsTitleWidget(model))); // 商品详情优惠劵 - WidgetFactory.regist('product_detail_coupon', - DefaultWidgetCreater((model) => CounponWidget(model))); + WidgetFactory.regist('product_detail_coupon', DefaultWidgetCreater((model) => CounponWidget(model))); // 商品详情店铺 - WidgetFactory.regist('product_detail_shop', - DefaultWidgetCreater((model) => StoreWidget(model))); + WidgetFactory.regist('product_detail_shop', DefaultWidgetCreater((model) => StoreWidget(model))); // 商品详情宝贝评价 - WidgetFactory.regist('product_detail_comment', - DefaultWidgetCreater((model) => GoodsDetailsEvaluateWidget(model))); + WidgetFactory.regist('product_detail_comment', DefaultWidgetCreater((model) => GoodsDetailsEvaluateWidget(model))); // 商品详情图片 - WidgetFactory.regist('product_detail_img_list', - DefaultWidgetCreater((model) => GoodsDetailsImgWidget(model))); + WidgetFactory.regist('product_detail_img_list', DefaultWidgetCreater((model) => GoodsDetailsImgWidget(model))); // 商品详情底部推荐列表 - WidgetFactory.regist( - 'product_detail_bottom_rec', GoodsDetailCommendCreater()); + WidgetFactory.regist('product_detail_bottom_rec', GoodsDetailCommendCreater()); // 商品详情底部 - WidgetFactory.regist('product_detail_bottom', - DefaultWidgetCreater((model) => GoodsDetailsFooterWidget(model))); + WidgetFactory.regist('product_detail_bottom', DefaultWidgetCreater((model) => GoodsDetailsFooterWidget(model))); // ==================== 个人中心 WidgetFactory.regist('profile_appbar', MineNavCreater()); - WidgetFactory.regist('profile_background', - DefaultWidgetCreater((model) => MineNavBg(model))); - WidgetFactory.regist( - 'profile_header', DefaultWidgetCreater((model) => MineHeader(model))); - WidgetFactory.regist( - 'profile_earning', DefaultWidgetCreater((model) => MineData(model))); - WidgetFactory.regist('profile_functions', - DefaultWidgetCreater((model) => MineQuickEntry(model))); - WidgetFactory.regist('profile_my_functions', - DefaultWidgetCreater((model) => MineQuickEntry(model))); - WidgetFactory.regist('profile_carousel', - DefaultWidgetCreater((model) => HomeBannerWidget(model))); + WidgetFactory.regist('profile_background', DefaultWidgetCreater((model) => MineNavBg(model))); + WidgetFactory.regist('profile_header', DefaultWidgetCreater((model) => MineHeader(model))); + WidgetFactory.regist('profile_earning', DefaultWidgetCreater((model) => MineData(model))); + WidgetFactory.regist('profile_functions', DefaultWidgetCreater((model) => MineQuickEntry(model))); + WidgetFactory.regist('profile_my_functions', DefaultWidgetCreater((model) => MineQuickEntry(model))); + WidgetFactory.regist('profile_carousel', DefaultWidgetCreater((model) => HomeBannerWidget(model))); // ==================== 钱包 WidgetFactory.regist( diff --git a/lib/widgets/goods_details/detail_img/bloc/goods_details_img_repository.dart b/lib/widgets/goods_details/detail_img/bloc/goods_details_img_repository.dart index cced390..5e57bfd 100644 --- a/lib/widgets/goods_details/detail_img/bloc/goods_details_img_repository.dart +++ b/lib/widgets/goods_details/detail_img/bloc/goods_details_img_repository.dart @@ -24,8 +24,9 @@ class GoodsDetailsImgRepository { try { if(!EmptyUtil.isEmpty(_model)) { List imgs = await TaobaoLoader.loadImages(_model.good_id); - _model.image_detail_list = imgs; - return _model; + if(!EmptyUtil.isEmpty(imgs)) { + return _model.copyWith(_model.image_detail_list + imgs); + } } }catch(e){ diff --git a/lib/widgets/goods_details/detail_img/model/goods_details_img_model.dart b/lib/widgets/goods_details/detail_img/model/goods_details_img_model.dart index d2631db..b7c8b4d 100644 --- a/lib/widgets/goods_details/detail_img/model/goods_details_img_model.dart +++ b/lib/widgets/goods_details/detail_img/model/goods_details_img_model.dart @@ -26,6 +26,17 @@ class GoodsDetailsImgModel { ); } + GoodsDetailsImgModel copyWith(List newImgs){ + return GoodsDetailsImgModel( + image_detail_list: newImgs, + title: title, + title_color: title_color, + provider: provider, + good_id: good_id, + icon: icon, + ); + } + Map toJson() { final Map data = new Map(); data['title'] = this.title; diff --git a/lib/widgets/goods_details/footer/goods_details_footer_widget.dart b/lib/widgets/goods_details/footer/goods_details_footer_widget.dart index c5b37d3..2b714e9 100644 --- a/lib/widgets/goods_details/footer/goods_details_footer_widget.dart +++ b/lib/widgets/goods_details/footer/goods_details_footer_widget.dart @@ -40,7 +40,13 @@ class GooddsDetailsFooterContainer extends StatefulWidget { class _GooddsDetailsFooterContainerState extends State { /// 打开首页 - void _openHome() {} + void _openHome() { + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute(builder: (BuildContext context) => PageFactory.create('homePage', null)), + (Route route) => false, + ); + } /// 收藏 void _collectOnClick() {} diff --git a/lib/widgets/home/home_goods/models/home_goods_model.dart b/lib/widgets/home/home_goods/models/home_goods_model.dart index acdf9c6..334ebed 100644 --- a/lib/widgets/home/home_goods/models/home_goods_model.dart +++ b/lib/widgets/home/home_goods/models/home_goods_model.dart @@ -1,4 +1,6 @@ -class HomeGoodsModel { +import 'package:equatable/equatable.dart'; + +class HomeGoodsModel extends Equatable { String provider; String providerName; String goodId; @@ -53,5 +55,8 @@ class HomeGoodsModel { data['inorder_count'] = this.inorderCount; return data; } + + @override + List get props => [this.goodId, this.provider, this.providerName]; } diff --git a/lib/widgets/home/home_sreach/home_sreach_creater.dart b/lib/widgets/home/home_sreach/home_sreach_creater.dart index cd63df2..952819f 100644 --- a/lib/widgets/home/home_sreach/home_sreach_creater.dart +++ b/lib/widgets/home/home_sreach/home_sreach_creater.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:zhiying_base_widget/widgets/home/home_sreach/home_sreach_widget.dart'; -import 'package:zhiying_base_widget/widgets/others/normal_nav/normal_nav.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; class HomeSreachCreater extends WidgetCreater { diff --git a/lib/widgets/home/home_sreach/home_sreach_widget.dart b/lib/widgets/home/home_sreach/home_sreach_widget.dart index cd3e64b..4538e24 100644 --- a/lib/widgets/home/home_sreach/home_sreach_widget.dart +++ b/lib/widgets/home/home_sreach/home_sreach_widget.dart @@ -3,6 +3,7 @@ import 'dart:ui'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/pages/search_page/search_page.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; class HomeSreachDeleagater extends SliverPersistentHeaderDelegate { @@ -67,6 +68,15 @@ class _HomeSreachContainer extends StatelessWidget { const _HomeSreachContainer(this.model); + /// 打开搜索页 + void _openSreach(BuildContext context){ + // SkipModel skipModel = SkipModel.fromJson(model); + // RouterUtil.route(skipModel, null, context); + Navigator.push(context, MaterialPageRoute( + builder: (context) => SearchPage(null) + )); + } + @override Widget build(BuildContext context) { List widgets = List(); @@ -94,10 +104,7 @@ class _HomeSreachContainer extends StatelessWidget { widgets.add(Expanded( child: GestureDetector( child: _getSreachWidget(), - onTap: () { - SkipModel skipModel = SkipModel.fromJson(model); - RouterUtil.route(skipModel, null, context); - }, + onTap: () => _openSreach(context), ), )); diff --git a/lib/widgets/search/history_tag/history_tag.dart b/lib/widgets/search/history_tag/history_tag.dart deleted file mode 100644 index 3261969..0000000 --- a/lib/widgets/search/history_tag/history_tag.dart +++ /dev/null @@ -1,129 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:zhiying_base_widget/widgets/search/widget/text_tag_widget.dart'; -import 'package:zhiying_base_widget/widgets/search/widget/title_widget.dart'; -import 'package:zhiying_comm/zhiying_comm.dart'; - -/// -/// 历史搜索标签 -/// -class HistoryTagWidget extends StatefulWidget { - final Map data; - - const HistoryTagWidget(this.data); - - @override - _HistoryTagWidgetState createState() => _HistoryTagWidgetState(); -} - -class _HistoryTagWidgetState extends State { - ///文本标签集合 - List _tagList = []; - static final String SP_HOISTROY_KEY = 'hoistroyTag'; - static final int MAX_COUNT = 6; - - /// 点击历史标签 - void _historyTagClick(String tag) {} - - /// 初始化历史搜索标签 - _initHistoryTag() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - String jsonStr = prefs.getString(SP_HOISTROY_KEY); - if (null != jsonStr && jsonStr.length > 0) { - final Map jsonMap = jsonDecode(jsonStr); - if (jsonMap != null && jsonMap.length > 0) { - jsonMap.forEach((key, value) => _tagList.insert(0, value)); - } - } - } - - /// 添加搜索 - void _addTag(String tag) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - String jsonStr = prefs.getString(SP_HOISTROY_KEY); - if (null != jsonStr && jsonStr.length > 0) { - final Map jsonMap = jsonDecode(jsonStr); - jsonMap[tag.toString()] = tag.toString(); - - _tagList.insert(0, tag); // 第一位 - if (_tagList.length > MAX_COUNT) { - jsonMap.remove(_tagList[_tagList.length - 1]); - _tagList.removeAt(_tagList.length - 1); - } - - prefs.setString(SP_HOISTROY_KEY, jsonEncode(jsonMap)); - } else { - prefs.setString(SP_HOISTROY_KEY, jsonEncode({'${tag.toString()}': '${tag.toString()}'})); - _tagList.add(tag); - } - setState(() {}); - } - - /// 点击清除所有历史记录 - void _clearTag() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - prefs.setString(SP_HOISTROY_KEY, ''); - } - - @override - void initState() { - _initHistoryTag(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Container( - width: double.infinity, - margin: const EdgeInsets.only(left: 12.5, right: 12.5), - child: Column( - children: [ - /// 标题 - SearchTitleWidget(titleText: '历史搜索', titleTextColor: '#333333', iconUrl: '', callback: () => _clearTag()), - const SizedBox( - height: 10, - ), - - /// 历史标签 - _getHistoryWarp(), - ], - ), - ); - } - - /// 历史搜索的标签 - Widget _getHistoryWarp() { - List itemWidgetList = []; - final int tagListLength = _tagList?.length ?? 0; - if (tagListLength > 0) { - for (var i = 0; i < _tagList.length; i++) { - var str = _tagList[i]; - itemWidgetList.add(GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => _historyTagClick(_tagList[i]), - child: TextTagWidget( - "$str", - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 10), - margin: const EdgeInsets.only(right: 5, bottom: 0, top: 0), - borderColor: HexColor.fromHex('#EDEDED'), - backgroundColor: Colors.white, - textStyle: TextStyle( - color: HexColor.fromHex('#383838'), - fontSize: 12.5, - ), - ), - )); - } - - return Wrap( - spacing: 8.0, - runSpacing: 8.0, - - ///子标签 - children: itemWidgetList); - } - return Container(); - } -} diff --git a/lib/widgets/search/history_tag/model/search_history_model.dart b/lib/widgets/search/history_tag/model/search_history_model.dart new file mode 100644 index 0000000..a7f42b2 --- /dev/null +++ b/lib/widgets/search/history_tag/model/search_history_model.dart @@ -0,0 +1,23 @@ +class SearchHistoryModel { + String clear_history_icon; + String history_bg_color; + String history_tilte; + + SearchHistoryModel({this.clear_history_icon, this.history_bg_color, this.history_tilte}); + + factory SearchHistoryModel.fromJson(Map json) { + return SearchHistoryModel( + clear_history_icon: json['clear_history_icon'], + history_bg_color: json['history_bg_color'], + history_tilte: json['history_tilte'], + ); + } + + Map toJson() { + final Map data = new Map(); + data['clear_history_icon'] = this.clear_history_icon; + data['history_bg_color'] = this.history_bg_color; + data['history_tilte'] = this.history_tilte; + return data; + } +} diff --git a/lib/widgets/search/history_tag/search_history_tag.dart b/lib/widgets/search/history_tag/search_history_tag.dart new file mode 100644 index 0000000..07b8173 --- /dev/null +++ b/lib/widgets/search/history_tag/search_history_tag.dart @@ -0,0 +1,167 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:zhiying_base_widget/pages/search_page/notifier/search_tag_notifier.dart'; +import 'package:zhiying_base_widget/pages/search_result_page/search_result_page.dart'; +import 'package:zhiying_base_widget/widgets/search/history_tag/model/search_history_model.dart'; +import 'package:zhiying_base_widget/widgets/search/widget/text_tag_widget.dart'; +import 'package:zhiying_base_widget/widgets/search/widget/title_widget.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:provider/provider.dart'; + +/// +/// 历史搜索标签 +/// +class SearchHistoryTagWidget extends StatefulWidget { + final Map data; + SearchHistoryModel model; + + SearchHistoryTagWidget(this.data, {Key key}) : super(key: key) { + try { + model = SearchHistoryModel.fromJson(jsonDecode(data['data'])); + } catch (e) {} + } + + @override + _SearchHistoryTagWidgetState createState() => _SearchHistoryTagWidgetState(); +} + +class _SearchHistoryTagWidgetState extends State { + ///文本标签集合 + List _tagList = []; + + /// 保存历史标签的sp key + static final String SP_HOISTROY_KEY = 'hoistroyTag'; + + /// 最大存储条数 + static final int MAX_COUNT = 10; + + /// 点击历史标签 + void _historyTagClick(String tag) { + if (!EmptyUtil.isEmpty(tag)) { + Navigator.push(context, MaterialPageRoute(builder: (_) => SearchResultPage({'keywords': tag ?? ''}))); + } + } + + // /// 初始化历史搜索标签 + // _initHistoryTag() async { + // SharedPreferences prefs = await SharedPreferences.getInstance(); + // String jsonStr = prefs.getString(SP_HOISTROY_KEY); + // if (null != jsonStr && jsonStr.length > 0) { + // final Map jsonMap = jsonDecode(jsonStr); + // if (jsonMap != null && jsonMap.length > 0) { + // jsonMap.forEach((key, value) => _tagList.insert(0, value)); + // } + // setState(() {}); + // } + // } + + /// 添加搜索 + // void _addTag(String tag) async { + // SharedPreferences prefs = await SharedPreferences.getInstance(); + // String jsonStr = prefs.getString(SP_HOISTROY_KEY); + // if (null != jsonStr && jsonStr.length > 0) { + // final Map jsonMap = jsonDecode(jsonStr); + // jsonMap[tag.toString()] = tag.toString(); + // + // _tagList.insert(0, tag); // 第一位 + // if (_tagList.length > MAX_COUNT) { + // jsonMap.remove(_tagList[_tagList.length - 1]); + // _tagList.removeAt(_tagList.length - 1); + // } + // + // prefs.setString(SP_HOISTROY_KEY, jsonEncode(jsonMap)); + // } else { + // prefs.setString(SP_HOISTROY_KEY, jsonEncode({'${tag.toString()}': '${tag.toString()}'})); + // _tagList.add(tag); + // } + // setState(() {}); + // } + + /// 点击清除所有历史记录 + void _clearTag() async { + await Provider.of(context, listen: false).clear(); + setState(() { + + }); + } + + @override + void initState() { + _initHistoryTag(); + super.initState(); + } + + _initHistoryTag() async { + _tagList = await Provider.of(context, listen: false).getHistoryTag(); + setState(() { + }); + } + + @override + void didChangeDependencies() { + _initHistoryTag(); + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + margin: const EdgeInsets.only(left: 12.5, right: 12.5, top: 15), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// 标题 + SearchTitleWidget( + titleText: widget?.model?.history_tilte ?? '历史搜索', + titleTextColor: widget?.model?.history_bg_color ?? '#333333', + iconUrl: widget?.model?.clear_history_icon ?? '', + callback: () => _clearTag()), + const SizedBox( + height: 10, + ), + + /// 历史标签 + _getHistoryWarp(), + ], + ), + ); + } + + /// 历史搜索的标签 + Widget _getHistoryWarp() { + List itemWidgetList = []; + final int tagListLength = _tagList?.length ?? 0; + if (tagListLength > 0) { + for (var i = 0; i < _tagList.length; i++) { + var str = _tagList[i]; + itemWidgetList.add(GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _historyTagClick(_tagList[i]), + child: TextTagWidget( + "$str", + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 10), + margin: const EdgeInsets.only(right: 5, bottom: 0, top: 0), + borderColor: HexColor.fromHex('#EDEDED'), + backgroundColor: Colors.white, + textStyle: TextStyle( + color: HexColor.fromHex('#383838'), + fontSize: 12.5, + ), + ), + )); + } + + return Wrap( + spacing: 8.0, + runSpacing: 8.0, + + ///子标签 + children: itemWidgetList); + } + return Container(); + } +} diff --git a/lib/widgets/search/hot_tag/model/search_hot_tag_model.dart b/lib/widgets/search/hot_tag/model/search_hot_tag_model.dart new file mode 100644 index 0000000..9d6d8a4 --- /dev/null +++ b/lib/widgets/search/hot_tag/model/search_hot_tag_model.dart @@ -0,0 +1,55 @@ +class SearchHotTagModel { + String hot_title; + String hot_title_color; + List search_hosts; + + SearchHotTagModel({this.hot_title, this.hot_title_color, this.search_hosts}); + + factory SearchHotTagModel.fromJson(Map json) { + return SearchHotTagModel( + hot_title: json['hot_title'], + hot_title_color: json['hot_title_color'], + search_hosts: json['search_hosts'] != null ? (json['search_hosts'] as List).map((i) => SearchHostTagItemModel.fromJson(i)).toList() : null, + ); + } + + Map toJson() { + final Map data = new Map(); + data['hot_title'] = this.hot_title; + data['hot_title_color'] = this.hot_title_color; + if (this.search_hosts != null) { + data['search_hosts'] = this.search_hosts.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class SearchHostTagItemModel { + String hot_icon; + String is_hot; + String keyword; + String keyword_background_color; + String keyword_color; + + SearchHostTagItemModel({this.hot_icon, this.is_hot, this.keyword, this.keyword_background_color, this.keyword_color}); + + factory SearchHostTagItemModel.fromJson(Map json) { + return SearchHostTagItemModel( + hot_icon: json['hot_icon'], + is_hot: json['is_hot'], + keyword: json['keyword'], + keyword_background_color: json['keyword_background_color'], + keyword_color: json['keyword_color'], + ); + } + + Map toJson() { + final Map data = new Map(); + data['hot_icon'] = this.hot_icon; + data['is_hot'] = this.is_hot; + data['keyword'] = this.keyword; + data['keyword_background_color'] = this.keyword_background_color; + data['keyword_color'] = this.keyword_color; + return data; + } +} diff --git a/lib/widgets/search/hot_tag/search_hot_tag_widget.dart b/lib/widgets/search/hot_tag/search_hot_tag_widget.dart new file mode 100644 index 0000000..c2cbac0 --- /dev/null +++ b/lib/widgets/search/hot_tag/search_hot_tag_widget.dart @@ -0,0 +1,100 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/pages/search_page/notifier/search_tag_notifier.dart'; +import 'package:zhiying_base_widget/pages/search_result_page/search_result_page.dart'; +import 'package:zhiying_base_widget/widgets/search/hot_tag/model/search_hot_tag_model.dart'; +import 'package:zhiying_base_widget/widgets/search/widget/text_tag_widget.dart'; +import 'package:zhiying_base_widget/widgets/search/widget/title_widget.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:provider/provider.dart'; + +/// +/// 搜索页面,热门搜索的标签 +/// +class SearchHotTagWidget extends StatelessWidget { + final Map data; + SearchHotTagModel model; + + SearchHotTagWidget(this.data, {Key key}) : super(key: key) { + try { + model = SearchHotTagModel.fromJson(jsonDecode(data['data'])); + } catch (e) { + Logger.error(e); + } + } + + /// 点击事件 + void _tagOnClick(BuildContext context, SearchHostTagItemModel model) async{ + print('${model?.keyword}'); + if (!EmptyUtil.isEmpty(model?.keyword)) { + + await Provider.of(context, listen: false).addTag(model.keyword); + + Navigator.push(context, MaterialPageRoute(builder: (_) => SearchResultPage({'keywords': model?.keyword ?? ''}))); + } + } + + @override + Widget build(BuildContext context) { + return _getMainWidget(context, model); + } + + /// 获取主视图 + Widget _getMainWidget(BuildContext context, SearchHotTagModel model) { + return Container( + width: double.infinity, + margin: const EdgeInsets.only(left: 12.5, right: 12.5, top: 15), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// 标题 + _getTitleWidget(model), + const SizedBox(height: 13), + _getTagWidget(context, model), + ], + ), + ); + } + + /// 标签widget + Widget _getTagWidget(BuildContext context, SearchHotTagModel model) { + List itemWidgetList = []; + final int tagListLength = model?.search_hosts?.length ?? 0; + if (tagListLength > 0) { + for (var i = 0; i < tagListLength; i++) { + var item = model.search_hosts[i]; + itemWidgetList.add(GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _tagOnClick(context, item), + child: TextTagWidget( + "${item?.keyword ?? ''}", + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 10), + margin: const EdgeInsets.only(right: 5, bottom: 0, top: 0), + borderColor: HexColor.fromHex(item?.keyword_background_color ?? '#FFFFFF'), + backgroundColor: HexColor.fromHex(item?.keyword_background_color ?? '#FFFFFF'), + textStyle: TextStyle(color: HexColor.fromHex(item?.keyword_color ?? '#383838'), fontSize: 12), + icon: item?.hot_icon ?? null, + ), + )); + } + + return Wrap( + spacing: 8.0, + runSpacing: 8.0, + + ///子标签 + children: itemWidgetList); + } + return Container(); + } + + /// 标题widget + Widget _getTitleWidget(SearchHotTagModel model) { + return SearchTitleWidget( + titleText: model?.hot_title ?? '热门搜索', + titleTextColor: model?.hot_title_color ?? '#333333', + ); + } +} diff --git a/lib/widgets/search/input/search_input_widget.dart b/lib/widgets/search/input/search_input_widget.dart index 8fbcf3e..8cdcb0e 100644 --- a/lib/widgets/search/input/search_input_widget.dart +++ b/lib/widgets/search/input/search_input_widget.dart @@ -1,11 +1,18 @@ import 'dart:convert'; - +import 'dart:ui'; import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/pages/search_page/notifier/search_tag_notifier.dart'; +import 'package:zhiying_base_widget/pages/search_result_page/search_result_page.dart'; +import 'package:zhiying_base_widget/pages/search_think_page/bloc/search_think_bloc.dart'; +import 'package:zhiying_base_widget/pages/search_think_page/search_think_page.dart'; import 'package:zhiying_base_widget/widgets/home/home_quick_entry/cached_network_image_util.dart'; import 'package:zhiying_base_widget/widgets/search/input/model/search_input_model.dart'; import 'package:zhiying_base_widget/widgets/search/input/search_input_sk.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:provider/provider.dart'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fluttertoast/fluttertoast.dart'; /// /// 搜索页的搜索框 @@ -28,7 +35,35 @@ class SearchInputWidget extends StatefulWidget { class _SearchInputWidgetState extends State { /// 点击搜索按钮 - void _onSearchButtomClick() {} + void _onSearchButtomClick() async { + String content = _editingController?.text?.toString()?.trim() ?? ''; + /// TODO 保存历史标签 + if (!EmptyUtil.isEmpty(content)) { + await Provider.of(context, listen: false).addTag(content); + Navigator.push(context, MaterialPageRoute(builder: (_) => SearchResultPage({'keywords': content}))); + }else{ + Fluttertoast.showToast(msg: '输入内容不能为空!'); + } + } + + /// 【弃用】打开搜索联想页面 + void _openSearchThinkPage(){ + Navigator.push(context, MaterialPageRoute( + builder: (_)=> SearchThinkPage({}) + )); + } + + /// 搜索框值改变 + void _searchInputChange(String text){ + if(!EmptyUtil.isEmpty(text)){ + // 进行网络更新 + print('输入框的内容是 $text'); + BlocProvider.of(context).add(SearchThinkKeyWrodsChangeEvent(text)); + }else{ + /// 输入框为空的时候,隐藏联想视图,显示原本的视图 + BlocProvider.of(context).add(SearchThinkShowBaseViewEvent()); + } + } FocusNode _focusNode; TextEditingController _editingController; @@ -65,32 +100,63 @@ class _SearchInputWidgetState extends State { /// 获取主视图 Widget _getMainWidget(SearchInputModel model) { return Container( + color: Colors.white, width: double.infinity, - height: 32, - margin: const EdgeInsets.only( - left: 12.5, - right: 12.5, - ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(25), - color: HexColor.fromHex('#F9F9F9'), - ), + margin: EdgeInsets.only(top: MediaQueryData.fromWindow(window).padding.top + 7.5, left: 12.5, right: 12.5), child: Row( children: [ - /// 搜索icon - _getSearchIconWidget(model), - const SizedBox(width: 7.5), - - /// 搜索输入框 - Expanded(child: _getSearchInputWidget(model)), - - /// 搜索按钮 - _getSearchButtomWidget(model), + /// 返回键 + _getReturnWidget(), + const SizedBox(width: 8.5), + /// 输入框 + Expanded( + child: Container( + width: double.infinity, + height: 32, + // margin: const EdgeInsets.only( + // left: 12.5, + // right: 12.5, + // ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(25), + color: HexColor.fromHex('#F9F9F9'), + ), + child: Row( + children: [ + /// 搜索icon + _getSearchIconWidget(model), + const SizedBox(width: 7.5), + + /// 搜索输入框 + Expanded(child: _getSearchInputWidget(model)), + + /// 搜索按钮 + _getSearchButtomWidget(model), + ], + ), + ), + ), ], ), ); } + /// 返回键 + Widget _getReturnWidget(){ + return GestureDetector( + onTap: ()=> Navigator.maybePop(context), + child: Container( + child: Icon( + Icons.arrow_back_ios, + size: 22, + color: HexColor.fromHex('#333333'), + ), + // onPressed: () => Navigator.maybePop(context), + ), + ); + } + + /// 搜索icon Widget _getSearchIconWidget(SearchInputModel model) { return Container( @@ -109,10 +175,18 @@ class _SearchInputWidgetState extends State { return Container( height: double.infinity, alignment: Alignment.centerLeft, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(30), color: HexColor.fromHex('#F9F9F9')), // padding: const EdgeInsets.symmetric(vertical: 6), child: TextField( showCursor: true, + // readOnly: true, + // onTap: ()=> _openSearchThinkPage(), + onChanged: (val)=> _searchInputChange(val), cursorWidth: 1, + onSubmitted: (text) => _onSearchButtomClick(), + controller: _editingController, + focusNode: _focusNode, + cursorColor: Colors.transparent, style: TextStyle(fontSize: 14, color: HexColor.fromHex('#333333')), decoration: InputDecoration( filled: false, @@ -124,7 +198,7 @@ class _SearchInputWidgetState extends State { errorBorder: InputBorder.none, disabledBorder: InputBorder.none, enabledBorder: InputBorder.none, - hintText: '搜索更多优惠商品', + hintText: model?.search_inpu_hint_text ?? '搜索更多优惠商品', hintStyle: TextStyle(color: HexColor.fromHex('#999999'), fontSize: 14), ), ), @@ -139,11 +213,14 @@ class _SearchInputWidgetState extends State { child: Container( padding: const EdgeInsets.symmetric(horizontal: 17.5, vertical: 6), decoration: BoxDecoration( - gradient: LinearGradient(colors: [HexColor.fromHex('#FD5E5E'), HexColor.fromHex('#FF0100')], begin: Alignment.centerLeft, end: Alignment.centerRight), + gradient: LinearGradient( + colors: [HexColor.fromHex(model?.search_button_color ?? '#FD5E5E'), HexColor.fromHex(model?.search_button_t ?? '#FF0100')], + begin: Alignment.centerLeft, + end: Alignment.centerRight), borderRadius: BorderRadius.circular(30), ), child: Text( - '搜索', + model?.search_button ?? '搜索', style: TextStyle(color: HexColor.fromHex('#FFFFFF'), fontSize: 14), ), ), diff --git a/lib/widgets/search/tabbar/model/search_tab_model.dart b/lib/widgets/search/tabbar/model/search_tab_model.dart index 99ff3c2..c83642c 100644 --- a/lib/widgets/search/tabbar/model/search_tab_model.dart +++ b/lib/widgets/search/tabbar/model/search_tab_model.dart @@ -26,8 +26,18 @@ class SearchTabItemModel { String name_select_color; String type; String with_icon_color; + String skip_identifier; - SearchTabItemModel({this.icon, this.line_select_color, this.name, this.name_color, this.name_select_color, this.type, this.with_icon_color}); + SearchTabItemModel({ + this.icon, + this.line_select_color, + this.name, + this.name_color, + this.name_select_color, + this.type, + this.with_icon_color, + this.skip_identifier, + }); factory SearchTabItemModel.fromJson(Map json) { return SearchTabItemModel( @@ -38,6 +48,7 @@ class SearchTabItemModel { name_select_color: json['name_select_color'], type: json['type'], with_icon_color: json['with_icon_color'], + skip_identifier: json['skip_identifier'], ); } @@ -50,6 +61,7 @@ class SearchTabItemModel { data['name_select_color'] = this.name_select_color; data['type'] = this.type; data['with_icon_color'] = this.with_icon_color; + data['skip_identifier'] = this.skip_identifier; return data; } } diff --git a/lib/widgets/search/tabbar/search_tab_creater.dart b/lib/widgets/search/tabbar/search_tab_creater.dart index b18f337..7a65ca5 100644 --- a/lib/widgets/search/tabbar/search_tab_creater.dart +++ b/lib/widgets/search/tabbar/search_tab_creater.dart @@ -13,12 +13,12 @@ class SearcchTabCreater extends WidgetCreater{ // SearchTabWidget(model), // ), // ), - SearchTabWidget(model), + Expanded(child: SearchTabWidget(model)), ]; } @override bool isSliverChild() { - return true; + return false; } } \ No newline at end of file diff --git a/lib/widgets/search/tabbar/search_tab_widget.dart b/lib/widgets/search/tabbar/search_tab_widget.dart index 9bc1bf6..bb0affb 100644 --- a/lib/widgets/search/tabbar/search_tab_widget.dart +++ b/lib/widgets/search/tabbar/search_tab_widget.dart @@ -3,14 +3,18 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:tab_indicator_styler/tab_indicator_styler.dart'; +import 'package:zhiying_base_widget/pages/search_result_page/search_result_page.dart'; +import 'package:zhiying_base_widget/pages/search_think_page/bloc/search_think_bloc.dart'; +import 'package:zhiying_base_widget/pages/search_think_page/model/search_think_model.dart'; import 'package:zhiying_base_widget/widgets/home/home_quick_entry/cached_network_image_util.dart'; import 'package:zhiying_base_widget/widgets/search/tabbar/search_tab_sk.dart'; import 'package:zhiying_base_widget/widgets/search/widget/my_tab.dart'; - +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; - +import 'package:flutter_bloc/flutter_bloc.dart'; import 'model/search_tab_model.dart'; + class SearchTabWidget extends StatefulWidget { final Map data; SearchTabModel model; @@ -29,12 +33,25 @@ class SearchTabWidget extends StatefulWidget { class _SearchTabWidgetState extends State { TabController _tabController; - int _currentIndex = 0; + + /// 联想列表的item点击事件 + _onThinkItemClick(SearchThinkModel model){ + Navigator.push(context, MaterialPageRoute( + builder: (_)=> SearchResultPage(model.toJson()) + )); + } @override void initState() { + _tabController = TabController(length: widget?.model?.search_icon_list?.length ?? 0, vsync: ScrollableState())..addListener((){ - setState(()=> _currentIndex = _tabController.index); + String type = ''; + try{ + type = widget.model.search_icon_list[_tabController.index].type; + }catch(_){} + if(!EmptyUtil.isEmpty(type)) { + BlocProvider.of(context).add(SearchThinkChangeTypeEvent(type)); + } }); super.initState(); } @@ -62,44 +79,110 @@ class _SearchTabWidgetState extends State { /// 获取TabBar Widget _getTabar(SearchTabModel model) { return Container( - margin: const EdgeInsets.only(left: 12.5, right: 12.5, top: 20), - child: TabBar( - controller: _tabController, - isScrollable: true, - labelStyle: TextStyle( fontSize: 14), - unselectedLabelColor: HexColor.fromHex('#999999'), - labelColor: HexColor.fromHex('#FF4242'), - // indicatorSize: TabBarIndicatorSize.label, - indicator: MaterialIndicator( - height: 2.5, - topLeftRadius: 8, - topRightRadius: 8, - bottomLeftRadius: 8, - bottomRightRadius: 8, - color: HexColor.fromHex('#FF4242'), - horizontalPadding: 25, - ), - tabs: model.search_icon_list.map((item) { - return MyTab( - icon: CachedNetworkImage(imageUrl: item?.with_icon_color ?? '', width: 14,), - text: item.name, - ); - }).toList(), + margin: const EdgeInsets.only(/*left: 12.5, right: 12.5,*/ top: 20), + child: Column( + children: [ + TabBar( + controller: _tabController, + isScrollable: true, + labelStyle: TextStyle( fontSize: 14, fontWeight: FontWeight.bold), + unselectedLabelColor: HexColor.fromHex('#999999'), + labelColor: HexColor.fromHex('#FF4242'), + // indicatorSize: TabBarIndicatorSize.label, + indicator: MaterialIndicator( + height: 2.5, + topLeftRadius: 8, + topRightRadius: 8, + bottomLeftRadius: 8, + bottomRightRadius: 8, + color: HexColor.fromHex('#FF4242'), + horizontalPadding: 25, + ), + tabs: model.search_icon_list.map((item) { + return MyTab( + icon: CachedNetworkImage(imageUrl: item?.with_icon_color ?? '', width: 14,), + text: item.name, + ); + }).toList(), + ), + + Expanded( + child: _getItemWidget(model), + ), + ], ), ); } -} + /// 根据输入框,是否显示联想列表还是其它 + Widget _getItemWidget(SearchTabModel model){ + return Stack( + children: [ + + _getTabBarView(model), + + BlocConsumer( + listener: (context, state){}, + buildWhen: (prev, current){ + if(current is SearchThinkErrorState){ + return false; + } + return true; + }, + builder: (context, state){ + + // return Visibility( + // replacement: _getTabBarView(model), + // + // child: _getThinkListWidget(), + // ); + + if(state is SearchThinkLoadedState){ + return _getThinkListWidget(state.model); + } + return Container(); + }, + ), + ], + ); + } -class SearchTabItemWidget extends StatelessWidget { - final bool isSelect; - final SearchTabItemModel model; + /// tabBarView + Widget _getTabBarView(SearchTabModel model){ + return TabBarView( + controller: _tabController, + children: model.search_icon_list.map((item){ + // TODO 这里需要和后台沟通改成页面的唯一标示 + return PageFactory.create('search_item_page', item.toJson()); + }).toList(), + ); + } - const SearchTabItemWidget(this.isSelect, this.model); - @override - Widget build(BuildContext context) { - return Container(); + /// 联想列表 + Widget _getThinkListWidget(List model){ + return Container( + color: Colors.white, + height: double.infinity, + child: ListView.builder(itemBuilder: (context, index){ + SearchThinkModel item = model[index]; + return GestureDetector( + onTap: ()=> _onThinkItemClick(item), + child: Container( + decoration: BoxDecoration( + border: Border(bottom: BorderSide(width: 0.5, color: HexColor.fromHex('#EEEEEE'))) + ), + padding: const EdgeInsets.only(top: 13, bottom: 13), + child: Text('${item?.keywords}', style: TextStyle( color: HexColor.fromHex('#333333'), fontSize: 14),), + ), + ); + }, + itemCount: model?.length ?? 0, + padding: const EdgeInsets.only(left: 12.5, right: 12.5), + + shrinkWrap: true, + ), + ); } -} +} diff --git a/lib/widgets/search/widget/my_tab.dart b/lib/widgets/search/widget/my_tab.dart index 7ac62b9..7b40c7e 100644 --- a/lib/widgets/search/widget/my_tab.dart +++ b/lib/widgets/search/widget/my_tab.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; const double _kTabHeight = 46.0; -const double _kTextAndIconTabHeight = 40.0; +const double _kTextAndIconTabHeight = 35.0; /// A material design [TabBar] tab. /// diff --git a/lib/widgets/search/widget/text_tag_widget.dart b/lib/widgets/search/widget/text_tag_widget.dart index 4d4b058..0332a9d 100644 --- a/lib/widgets/search/widget/text_tag_widget.dart +++ b/lib/widgets/search/widget/text_tag_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; - +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:zhiying_comm/util/empty_util.dart'; import 'color_utils.dart'; /** @@ -16,14 +17,13 @@ class TextTagWidget extends StatefulWidget { String text; EdgeInsets margin; EdgeInsets padding; - TextStyle textStyle; - Color backgroundColor; Color borderColor; - double borderRadius; + String icon; + TextTagWidget( this.text, { this.margin = const EdgeInsets.all(4), @@ -31,6 +31,7 @@ class TextTagWidget extends StatefulWidget { this.textStyle, this.backgroundColor, this.borderColor, + this.icon, this.borderRadius = 20.0, }) { if (this.borderColor == null) { @@ -62,10 +63,22 @@ class TextTagWidget extends StatefulWidget { class _TestPageState extends State { @override Widget build(BuildContext context) { + + if(EmptyUtil.isEmpty(widget.icon)) { + return buildTag(); + }else{ + return buildTag(); + } + } + + /// 构建按钮 + Widget buildTag(){ return Container( + margin: widget.margin, padding: widget.padding, - decoration: BoxDecoration(color: widget.backgroundColor, borderRadius: BorderRadius.all(Radius.circular(widget.borderRadius)), border: Border.all(color: widget.borderColor)), + decoration: BoxDecoration( + color: widget.backgroundColor, borderRadius: BorderRadius.all(Radius.circular(widget.borderRadius)), border: Border.all(color: widget.borderColor)), child: buildTextWidget(), ); } diff --git a/lib/widgets/search_result/goods_list/bloc/search_result_goods_list_bloc.dart b/lib/widgets/search_result/goods_list/bloc/search_result_goods_list_bloc.dart new file mode 100644 index 0000000..ac90f4f --- /dev/null +++ b/lib/widgets/search_result/goods_list/bloc/search_result_goods_list_bloc.dart @@ -0,0 +1,103 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:zhiying_base_widget/widgets/home/home_goods/models/home_goods_model.dart'; +import 'package:zhiying_base_widget/widgets/search_result/goods_list/bloc/search_result_goods_list_repository.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +part 'search_result_goods_list_event.dart'; + +part 'search_result_goods_list_state.dart'; + +class SearchResultGoodsListBloc extends Bloc { + // SearchResultGoodsListBloc() : super(SearchResultGoodsListInitial()); + + SearchResultGoodsListRepository repository; + + SearchResultGoodsListBloc({@required this.repository}); + + @override + SearchResultGoodsListState get initialState => SearchResultGoodsListInitial(); + + @override + Stream mapEventToState( + SearchResultGoodsListEvent event, + ) async* { + /// 初始化 + if (event is SearchResultGoodsListInitEvent) { + yield* _mapInitEventToState(event); + } + + /// 下拉刷新 + if (event is SearchResultGoodsListOnRefreshEvent) { + yield* _mapRefreshToState(event); + } + + /// 上拉更多 + if (event is SearchResultGoodsListOnLoadEvent && state is SearchResultGoodsListLoadedState) { + yield* _mapOnLoadToState(event, state); + } + + /// 修改样式 + if (event is SearchResultGoodsListChangeStyleEvent) { + yield* _mapChangeStyleToState(); + } + + /// 搜索 + if(event is SearchResultGoodsListSubmitEvent){ + yield* _mapSubmitToState(event); + } + } + + /// 初始化事件 + Stream _mapInitEventToState(SearchResultGoodsListInitEvent event) async* { + var result = await repository.fetchInitData(); + if (!EmptyUtil.isEmpty(result)) { + yield SearchResultGoodsListLoadedState(model: result); + } else { + yield SearchResultGoodsListErrorState(); + } + } + + /// 下拉刷新 + Stream _mapRefreshToState(SearchResultGoodsListOnRefreshEvent event) async* { + var result = await repository.fetchRefreshData(); + if (!EmptyUtil.isEmpty(result)) { + yield SearchResultGoodsListLoadedState(model: result); + } else { + yield SearchResultGoodsListErrorState(); + } + } + + /// 上拉更多 + Stream _mapOnLoadToState(SearchResultGoodsListOnLoadEvent event, SearchResultGoodsListLoadedState state) async* { + var result = await repository.fetchLoadData(); + if (!EmptyUtil.isEmpty(result)) { + yield SearchResultGoodsListLoadedState(model: result); + } else { + yield SearchResultGoodsListErrorState(); + } + } + + /// 修改样式 + Stream _mapChangeStyleToState() async* { + var result = repository.fetchChangeStyle(); + if (!EmptyUtil.isEmpty(result)) { + yield SearchResultGoodsListChangeStyleState(result['style'], result['datas']); + } else { + yield SearchResultGoodsListErrorState(); + } + } + + /// 关键字搜索 + Stream _mapSubmitToState(SearchResultGoodsListSubmitEvent event) async* { + var result = await repository.fetchSearchSubmit(event.keywords); + if (!EmptyUtil.isEmpty(result)) { + yield SearchResultGoodsListLoadedState(model: result); + } else { + yield SearchResultGoodsListErrorState(); + } + } +} diff --git a/lib/widgets/search_result/goods_list/bloc/search_result_goods_list_event.dart b/lib/widgets/search_result/goods_list/bloc/search_result_goods_list_event.dart new file mode 100644 index 0000000..679775e --- /dev/null +++ b/lib/widgets/search_result/goods_list/bloc/search_result_goods_list_event.dart @@ -0,0 +1,26 @@ +part of 'search_result_goods_list_bloc.dart'; + +abstract class SearchResultGoodsListEvent extends Equatable { + const SearchResultGoodsListEvent(); + + @override + List get props => []; +} + +/// 初始化数据 +class SearchResultGoodsListInitEvent extends SearchResultGoodsListEvent {} + +/// 下拉刷新 +class SearchResultGoodsListOnRefreshEvent extends SearchResultGoodsListEvent {} + +/// 上拉更多 +class SearchResultGoodsListOnLoadEvent extends SearchResultGoodsListEvent {} + +/// 修改样式 +class SearchResultGoodsListChangeStyleEvent extends SearchResultGoodsListEvent{} + +/// 关键字搜索 +class SearchResultGoodsListSubmitEvent extends SearchResultGoodsListEvent{ + final String keywords; + const SearchResultGoodsListSubmitEvent(this.keywords); +} \ No newline at end of file diff --git a/lib/widgets/search_result/goods_list/bloc/search_result_goods_list_repository.dart b/lib/widgets/search_result/goods_list/bloc/search_result_goods_list_repository.dart new file mode 100644 index 0000000..e47d0dc --- /dev/null +++ b/lib/widgets/search_result/goods_list/bloc/search_result_goods_list_repository.dart @@ -0,0 +1,107 @@ +import 'package:flutter/cupertino.dart'; +import 'package:zhiying_base_widget/widgets/home/home_goods/models/home_goods_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +class SearchResultGoodsListRepository { + SearchResultGoodsListRepository({@required this.reqData}); + + /// 当前页面 + int _currentPage = 1; + + /// 最大条数 + final int MAX = 10; + + /// 是否还有更多数据 + bool _hasNomore = true; + + /// 数据 + List _oldDatas = []; + + /// 显示样式 + bool _isShowOneColumn = true; + + /// 查询数据参数 + Map reqData = {}; + + /// 初始化 + Future> fetchInitData() async { + _currentPage = 1; + _hasNomore = true; + return _baseInitData(true, reqData); + } + + /// 下拉刷新 + Future> fetchRefreshData() async { + _currentPage = 1; + _hasNomore = true; + return _baseInitData(true,reqData); + } + + /// 上拉更多 + Future> fetchLoadData() async { + /// 只有更多数据的时候才进行加载 + if (_hasNomore) { + return _baseInitData(false, reqData); + } else { + return null; + } + } + + /// 修改显示样式 + Map fetchChangeStyle() { + _isShowOneColumn = !_isShowOneColumn; + return {'style': _isShowOneColumn, 'datas': _oldDatas}; + } + + /// 输入框修改关键字 + Future> fetchSearchSubmit(String keywords) async{ + if(!EmptyUtil.isEmpty(keywords)) { + reqData['reqData'] = keywords; + _currentPage = 1; + _hasNomore =true; + return _baseInitData(true, reqData); + } + return null; + } + + /// 查询 + Future> _baseInitData(bool refresh,final Map model) async { + String keyword = model['keywords'] ?? ''; + String type = model['type']; + if (EmptyUtil.isEmpty(keyword)) return null; + var result = await NetUtil.post('/api/v1/s/$type', + params: { + 'keyword': keyword, + 'size': MAX.toString(), + 'p': _currentPage.toString(), + }, + method: NetMethod.POST); + try { + // search_list + if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { + List data = List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['search_list']); + if (data.length >= MAX) { + _hasNomore = true; + ++_currentPage; + } + // 下拉刷新需要清理 + if(refresh){ + _oldDatas.clear(); + } + + data.forEach((item) { + _oldDatas.add(HomeGoodsModel.fromJson(item)); + }); + + List newData = []; + newData.addAll(_oldDatas); + + return newData; + } + } catch (e) { + Logger.error(e); + } + _hasNomore = false; + return null; + } +} diff --git a/lib/widgets/search_result/goods_list/bloc/search_result_goods_list_state.dart b/lib/widgets/search_result/goods_list/bloc/search_result_goods_list_state.dart new file mode 100644 index 0000000..fd29c8d --- /dev/null +++ b/lib/widgets/search_result/goods_list/bloc/search_result_goods_list_state.dart @@ -0,0 +1,42 @@ +part of 'search_result_goods_list_bloc.dart'; + +abstract class SearchResultGoodsListState extends Equatable { + const SearchResultGoodsListState(); + + @override + List get props => []; +} + +class SearchResultGoodsListInitial extends SearchResultGoodsListState { + @override + List get props => []; +} + +/// 数据加载完毕 +class SearchResultGoodsListLoadedState extends SearchResultGoodsListState { + List model; + + + SearchResultGoodsListLoadedState({@required this.model}); + + SearchResultGoodsListLoadedState copyWith(List newData) { + return SearchResultGoodsListLoadedState(model: newData ?? model); + } + + @override + List get props => [this.model]; +} + +/// 数据加载出错 +class SearchResultGoodsListErrorState extends SearchResultGoodsListState {} + +/// 切换样式成功 +class SearchResultGoodsListChangeStyleState extends SearchResultGoodsListState { + bool isShowOneColumn = true; + List model; + + SearchResultGoodsListChangeStyleState(this.isShowOneColumn, this.model); + + @override + List get props => [this.isShowOneColumn]; +} diff --git a/lib/pages/sreach_result_page/sreach_result_page.dart b/lib/widgets/search_result/goods_list/search_result_goods_list_sk.dart similarity index 58% rename from lib/pages/sreach_result_page/sreach_result_page.dart rename to lib/widgets/search_result/goods_list/search_result_goods_list_sk.dart index 8d70417..959af16 100644 --- a/lib/pages/sreach_result_page/sreach_result_page.dart +++ b/lib/widgets/search_result/goods_list/search_result_goods_list_sk.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; /// -/// 搜索结果页面 +/// 商品列表的骨架图 /// -class SreachResultPage extends StatelessWidget { +class SearchResultGoodsListSkeleton extends StatelessWidget { @override Widget build(BuildContext context) { return Container(); diff --git a/lib/widgets/search_result/goods_list/search_result_goods_list_widget.dart b/lib/widgets/search_result/goods_list/search_result_goods_list_widget.dart new file mode 100644 index 0000000..ba637fc --- /dev/null +++ b/lib/widgets/search_result/goods_list/search_result_goods_list_widget.dart @@ -0,0 +1,192 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/pages/goods_details_page/goods_details_page.dart'; +import 'package:zhiying_base_widget/widgets/home/home_goods/home_goods_item.dart'; +import 'package:zhiying_base_widget/widgets/home/home_goods/home_goods_item_single.dart'; +import 'package:zhiying_base_widget/widgets/home/home_goods/models/home_goods_model.dart'; +import 'package:zhiying_base_widget/widgets/home/home_goods/models/home_goods_style_model.dart'; +import 'package:zhiying_base_widget/widgets/home/home_goods/skeleton/home_goods_sk.dart'; +import 'package:zhiying_base_widget/widgets/search_result/goods_list/bloc/search_result_goods_list_bloc.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class SearchResultGoodsListWidget extends StatefulWidget { + final Map data; + HomeGoodsStyleModel style; + + SearchResultGoodsListWidget(this.data) { + try { + style = HomeGoodsStyleModel.fromJson(jsonDecode(data['data'])); + } catch (e) {} + } + + @override + _SearchResultGoodsListWidgetState createState() => _SearchResultGoodsListWidgetState(); +} + +class _SearchResultGoodsListWidgetState extends State { + @override + Widget build(BuildContext context) { + // return BlocProvider( + // create: (_) => SearchResultGoodsListBloc(repository: SearchResultGoodsListRepository())..add(SearchResultGoodsListInitEvent(model: widget?.data)), + // child: SearchResultGoodsListWidgetContainer(), + // ); + return SearchResultGoodsListWidgetContainer(widget?.data, widget?.style); + } +} + +class SearchResultGoodsListWidgetContainer extends StatefulWidget { + final Map data; + final HomeGoodsStyleModel style; + + const SearchResultGoodsListWidgetContainer(this.data, this.style); + + @override + _SearchResultGoodsListWidgetContainerState createState() => _SearchResultGoodsListWidgetContainerState(); +} + +/// 商品列表 +class _SearchResultGoodsListWidgetContainerState extends State { + /// 显示样式 + bool _isShowOneColumn = true; + + /// 下拉刷新 + void _onRefersh() async { + BlocProvider.of(context).add(SearchResultGoodsListOnRefreshEvent()); + } + + /// 上拉更多 + void _onLoad() async { + BlocProvider.of(context).add(SearchResultGoodsListOnLoadEvent()); + } + + /// 跳转商品详情 + void _openGoodsDetailsPage() { + Navigator.push(context, MaterialPageRoute(builder: (_) => GoodsDetailsPage(null))); + } + + @override + void initState() { + /// 初始化 + BlocProvider.of(context).add(SearchResultGoodsListInitEvent()); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return BlocConsumer( + listener: (BuildContext context, SearchResultGoodsListState state) { + if (state is SearchResultGoodsListErrorState) { + print('数据加载出错'); + } + }, + buildWhen: (previous, current) { + /// 数据加载出错不进行build + if (current is SearchResultGoodsListErrorState) { + return false; + } + + return true; + }, + builder: (context, state) { + if (state is SearchResultGoodsListLoadedState) { + return _getMainWidget(state?.model, widget?.style); + } + if (state is SearchResultGoodsListChangeStyleState) { + _isShowOneColumn = state.isShowOneColumn; + return _getMainWidget(state?.model, widget?.style); + } + + /// 骨架屏幕 + return HomeGoodsSkeleton(); + }, + ); + } + + /// 获取主视图 + Widget _getMainWidget(List goods, HomeGoodsStyleModel styleModel) { + int column = _isShowOneColumn ? 1 : 2; + + return ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: goods?.length ?? 0, + itemBuilder: (context, index) { + if (column == 1) { + return HomeGoodsItemSingle( + goods[index], + styleModel, + data: widget.data, + ); + } else { + // return Container(color: Colors.red, height: 126,margin: EdgeInsets.all(10),); + return Padding( + padding: const EdgeInsets.only(left: 5, right: 5), + child: Row( + children: List.generate(column, (c) { + int i = index * column + c; + return Expanded( + child: i < goods.length + ? HomeGoodsItem( + goods[i], + styleModel, + data: widget.data, + ) + : Container(), + ); + }).toList(), + ), + ); + } + }); + } + + /// 单列 +// Widget _getSignleWidget(List goods, HomeGoodsStyleModel styleModel) { +// return ListView.builder( +// shrinkWrap: true, +// physics: NeverScrollableScrollPhysics(), +// itemCount: goods.length, +// itemBuilder: (context, index) { +// if (column == 1) { +// return HomeGoodsItemSingle( +// goods[index], +// styleModel, +// data: widget.data, +// ); +// } else { +// // return Container(color: Colors.red, height: 126,margin: EdgeInsets.all(10),); +// return Padding( +// padding: const EdgeInsets.only(left: 5, right: 5), +// child: Row( +// children: List.generate(column, (c) { +// int i = index * column + c; +// return Expanded( +// child: i < goods.length +// ? HomeGoodsItem( +// goods[i], +// _style, +// data: widget.data, +// ) +// : Container(), +// ); +// }).toList(), +// ), +// ); +// } +// }); +// +// return HomeGoodsItemSingle(model, styleModel); +// } +// +// /// 双列 +// Widget _getTowWidget(List goods, HomeGoodsStyleModel styleModel) { +// int column = 2; +// int count = (goods.length / column).ceil(); +// +// return Container( +// child: Text('22222222'), +// ); +// } +} diff --git a/lib/widgets/search_result/search_input/model/search_result_input_model.dart b/lib/widgets/search_result/search_input/model/search_result_input_model.dart new file mode 100644 index 0000000..f7bfca1 --- /dev/null +++ b/lib/widgets/search_result/search_input/model/search_result_input_model.dart @@ -0,0 +1,39 @@ +class SearchResultInputModel { + String search_button; + String search_button_color; + String search_button_t; + String search_icon; + String search_inpu_hint_text; + String keywords; + + SearchResultInputModel({ + this.search_button, + this.search_button_color, + this.search_button_t, + this.search_icon, + this.search_inpu_hint_text, + this.keywords, + }); + + factory SearchResultInputModel.fromJson(Map json) { + return SearchResultInputModel( + search_button: json['search_button'], + search_button_color: json['search_button_color'], + search_button_t: json['search_button_t'], + search_icon: json['search_icon'], + search_inpu_hint_text: json['search_inpu_hint_text'], + keywords: json['keywords'], + ); + } + + Map toJson() { + final Map data = new Map(); + data['search_button'] = this.search_button; + data['search_button_color'] = this.search_button_color; + data['search_button_t'] = this.search_button_t; + data['search_icon'] = this.search_icon; + data['search_inpu_hint_text'] = this.search_inpu_hint_text; + data['keywords'] = this.keywords; + return data; + } +} diff --git a/lib/widgets/search_result/search_input/search_result_input.dart b/lib/widgets/search_result/search_input/search_result_input.dart new file mode 100644 index 0000000..8572ead --- /dev/null +++ b/lib/widgets/search_result/search_input/search_result_input.dart @@ -0,0 +1,160 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/widgets/search_result/goods_list/bloc/search_result_goods_list_bloc.dart'; +import 'package:zhiying_base_widget/widgets/search_result/search_input/model/search_result_input_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'dart:ui'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +/// +/// 搜索结果页面输入框 +/// +class SearchResultInputWidget extends StatefulWidget { + final Map data; + SearchResultInputModel model; + + SearchResultInputWidget(this.data, {Key key}) : super(key: key) { + try { + model = SearchResultInputModel.fromJson(jsonDecode(data['data'])); + } catch (e) { + Logger.error(e); + } + } + + @override + _SearchResultInputWidgetState createState() => _SearchResultInputWidgetState(); +} + +class _SearchResultInputWidgetState extends State { + TextEditingController _textEditingController; + FocusNode _focusNode; + + /// 返回事件 + void _openPop() { + Navigator.maybePop(context); + } + + /// 搜索事件 + void _onSearchButtomClick() { + print('搜索商品'); + String keywords = _textEditingController?.text?.toString()?.trim(); + if(!EmptyUtil.isEmpty(keywords)){ + // BlocProvider.of(context).add(SearchResultGoodsListSubmitEvent(keywords)); + } + } + + @override + void initState() { + _textEditingController = TextEditingController(text: widget?.model?.keywords ?? ''); + _focusNode = FocusNode(); + super.initState(); + } + + @override + void dispose() { + _focusNode?.unfocus(); + _focusNode?.dispose(); + _textEditingController?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + child: _getMainWidget(widget?.model), + ); + } + + /// 主视图 + Widget _getMainWidget(SearchResultInputModel model) { + return Container( + width: double.infinity, + margin: EdgeInsets.only(/*left: 12.5,*/ right: 12.5, top: MediaQueryData.fromWindow(window).padding.top), + // child: _getAppBar(model), + child: Row( + children: [ + /// 返回键 + _createReturnWidget(model), + + /// 输入框 + Expanded(child: _createInputWidget(model)), + + /// 搜索按钮 + _createSubmitButtomWidget(model), + ], + ), + ); + } + + /// 返回按钮 + Widget _createReturnWidget(SearchResultInputModel model) { + return IconButton( + icon: Icon( + Icons.arrow_back_ios, + size: 22, + color: HexColor.fromHex('#000000'), + + ), + onPressed: () => _openPop(), + ); + } + + /// 搜索按钮 + Widget _createSubmitButtomWidget(SearchResultInputModel model) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: ()=> _onSearchButtomClick(), + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.only( + top: 6, + bottom: 6, + left: 10, /*right: 12.5*/ + ), + child: Text( + model?.search_button ?? '搜索', + style: TextStyle(fontSize: 14, color: HexColor.fromHex(model?.search_button_color), fontWeight: FontWeight.bold), + ), + ), + ); + } + + /// 输入框 + Widget _createInputWidget(SearchResultInputModel model) { + return Container( + width: double.infinity, + child: Container( + height: 32, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30), + color: HexColor.fromHex('#F9F9F9'), + ), + padding: const EdgeInsets.only(left: 12.5, right: 12.5), + child: TextField( + onTap: ()=> Navigator.maybePop(context, _textEditingController?.text?.toString()?.trim()), + showCursor: true, + cursorWidth: 1, + onSubmitted: (text) => _onSearchButtomClick(), + controller: _textEditingController, + focusNode: _focusNode, + style: TextStyle(fontSize: 14, color: HexColor.fromHex('#333333')), + decoration: InputDecoration( + filled: false, + // focusColor: Colors.transparent, + // fillColor: Colors.transparent, + border: InputBorder.none, + focusedBorder: InputBorder.none, + focusedErrorBorder: InputBorder.none, + errorBorder: InputBorder.none, + disabledBorder: InputBorder.none, + enabledBorder: InputBorder.none, + hintText: model?.search_inpu_hint_text ?? '搜索更多优惠商品', + hintStyle: TextStyle(color: HexColor.fromHex('#999999'), fontSize: 14), + ), + ), + ), + ); + } + +} diff --git a/lib/widgets/search_result/sort/model/search_result_sort_model.dart b/lib/widgets/search_result/sort/model/search_result_sort_model.dart new file mode 100644 index 0000000..fd51b41 --- /dev/null +++ b/lib/widgets/search_result/sort/model/search_result_sort_model.dart @@ -0,0 +1,126 @@ +class SearchResultSortModel { + String coupin_icon; + String coupin_icon_with_color; + String layout_icon_1; + String layout_icon_2; + List sort_list; + String text_no_select_color; + String text_select_color; + + /// 是否展示1列 + bool isShowOneColumn; + String keywords; + + SearchResultSortModel({ + this.coupin_icon, + this.coupin_icon_with_color, + this.layout_icon_1, + this.layout_icon_2, + this.sort_list, + this.text_no_select_color, + this.text_select_color, + this.isShowOneColumn, + this.keywords, + }); + + factory SearchResultSortModel.fromJson(Map json) { + return SearchResultSortModel( + coupin_icon: json['coupin_icon'], + coupin_icon_with_color: json['coupin_icon_with_color'], + layout_icon_1: json['layout_icon_1'], + layout_icon_2: json['layout_icon_2'], + sort_list: json['sort_list'] != null ? (json['sort_list'] as List).map((i) => SearchResultItemSort.fromJson(i)).toList() : null, + text_no_select_color: json['text_no_select_color'], + text_select_color: json['text_select_color'], + isShowOneColumn: json['isShowOneColumn'] ?? false, + keywords: json['keywords'], + ); + } + + Map toJson() { + final Map data = new Map(); + data['coupin_icon'] = this.coupin_icon; + data['coupin_icon_with_color'] = this.coupin_icon_with_color; + data['layout_icon_1'] = this.layout_icon_1; + data['layout_icon_2'] = this.layout_icon_2; + data['text_no_select_color'] = this.text_no_select_color; + data['text_select_color'] = this.text_select_color; + if (this.sort_list != null) { + data['sort_list'] = this.sort_list.map((v) => v.toJson()).toList(); + } + data['isShowOneColumn'] = this.isShowOneColumn; + data['keywords'] = this.keywords; + return data; + } +} + +class SearchResultItemSort { + String name; + bool isSelect; + List select_list; + String type; + String icon; + String color_icon; + String color_icon_up; + String color_icon_down; + + SearchResultItemSort({ + this.name, + this.select_list, + this.isSelect, + this.type, + this.icon, + this.color_icon, + this.color_icon_up, + this.color_icon_down, + }); + + factory SearchResultItemSort.fromJson(Map json) { + return SearchResultItemSort( + name: json['name'], + isSelect: json['isSelect'] ?? false, + select_list: json['select_list'] != null ? (json['select_list'] as List).map((i) => SearchResultSortSelect.fromJson(i)).toList() : null, + type: json['type']?.toString(), + icon: json['icon']?.toString(), + color_icon: json['color_icon']?.toString(), + color_icon_up: json['color_icon_up']?.toString(), + color_icon_down: json['color_icon_down']?.toString(), + ); + } + + Map toJson() { + final Map data = new Map(); + data['name'] = this.name; + data['isSelect'] = this.isSelect; + if (this.select_list != null) { + data['select_list'] = this.select_list.map((v) => v.toJson()).toList(); + } + data['type'] = this.type; + data['icon'] = this.icon; + data['color_icon'] = this.color_icon; + data['color_icon_up'] = this.color_icon_up; + data['color_icon_down'] = this.color_icon_down; + return data; + } +} + +class SearchResultSortSelect { + String name; + String query_args; + + SearchResultSortSelect({this.name, this.query_args}); + + factory SearchResultSortSelect.fromJson(Map json) { + return SearchResultSortSelect( + name: json['name'], + query_args: json['query_args'], + ); + } + + Map toJson() { + final Map data = new Map(); + data['name'] = this.name; + data['query_args'] = this.query_args; + return data; + } +} diff --git a/lib/widgets/search_result/sort/search_result_sort_widget.dart b/lib/widgets/search_result/sort/search_result_sort_widget.dart new file mode 100644 index 0000000..80f7c77 --- /dev/null +++ b/lib/widgets/search_result/sort/search_result_sort_widget.dart @@ -0,0 +1,384 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/widgets/home/home_quick_entry/cached_network_image_util.dart'; +import 'package:zhiying_base_widget/widgets/search_result/goods_list/bloc/search_result_goods_list_bloc.dart'; +import 'package:zhiying_base_widget/widgets/search_result/sort/model/search_result_sort_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +/// +/// 搜索结果页排序widget +/// +class SearchResultSortWidget extends StatefulWidget { + final Map data; + SearchResultSortModel model; + + SearchResultSortWidget(this.data, {Key key}) : super(key: key) { + try { + model = SearchResultSortModel.fromJson(jsonDecode(data['data'])); + } catch (_) {} + } + + @override + _SearchResultSortWidgetState createState() => _SearchResultSortWidgetState(); +} + +class _SearchResultSortWidgetState extends State { + TextEditingController _startPriceEditingController; + TextEditingController _endPriceEditingController; + FocusNode _startPriceNode; + FocusNode _endPriceNode; + + // 是否展开综合 + bool _isShowZh = false; + // 切换显示样式 + bool _isShowOneColumn = true; + // 是否展开下拉选项 + bool _expand = false; + + /// 切换类型 + void _changeColumn() { + setState(() => _isShowOneColumn = !_isShowOneColumn); + BlocProvider.of(context).add(SearchResultGoodsListChangeStyleEvent()); + } + + /// 张开关闭下拉框 + void _openXLWidget(){ + setState(() { + _expand = !_expand; + }); + } + + /// 展开综合 + void _openZHPopupWidget() {} + + /// 展开筛选 + void _openSXWidget() {} + + /// 点击当前 + void _clickSortType(SearchResultItemSort model) { + + if(model.type != 'sort'){ + setState(() { + _expand = !_expand; + }); + } + } + + /// 关闭下拉框 + + + @override + void initState() { + _startPriceEditingController = TextEditingController(); + _endPriceEditingController = TextEditingController(); + _startPriceNode = FocusNode(); + _endPriceNode = FocusNode(); + super.initState(); + } + + @override + void dispose() { + _startPriceNode?.unfocus(); + _endPriceNode?.unfocus(); + _startPriceNode?.dispose(); + _endPriceNode?.dispose(); + _startPriceEditingController?.dispose(); + _endPriceEditingController?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + // height: 41, + width: double.infinity, + // color: Colors.red, + child: _getMainWidget(widget?.model), + ); + } + + /// 主视图 + Widget _getMainWidget(SearchResultSortModel model) { + return Column( + children: [ + /// 筛选栏 + Container( + height: 41, + width: double.infinity, + color: Colors.white, + padding: const EdgeInsets.only(left: 12.5, right: 12.5), + child: Row( + children: [ + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _createSortListWidget(model), + ), + ), + + /// 切换按钮 + _getChangeIconWidget(model), + ], + ), + ), + + /// 展开列表 + Visibility( + visible: _expand, + child: Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _openXLWidget(), + child: Container( + // height: double.infinity, + width: double.infinity, + decoration: BoxDecoration( + color: HexColor.fromHex('#33333333'), + ), + child: Visibility( + visible: _isShowZh, + replacement: _getZHWidget(model), + child: _getSXWidget(model), + ), + ), + ), + ), + ) + ], + ); + } + + /// ExpandedList 展开列表 + Widget _getZHWidget(SearchResultSortModel data) { + List model; + List lists = []; + int sort_list_length = data?.sort_list?.length ?? 0; + if (sort_list_length > 0) { + data.sort_list.forEach((item) { + if (item.type == 'popup') { + model = item.select_list; + } + }); + + for (int i = 0; i < model.length; i++) { + var item = model[i]; + double borderRadius = (i == model.length - 1) ? 12.5 : 0; + var padding = EdgeInsets.only(left: 12.5, right: 12.5, top: 10, bottom: (i == model.length - 1) ? 15 : 0); + + lists.add(Container( + padding: padding, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(width: 0, color: Colors.transparent), + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(borderRadius), + bottomLeft: Radius.circular(borderRadius), + )), + width: double.infinity, + child: Text( + item?.name ?? '', + style: TextStyle(fontSize: 12, color: HexColor.fromHex(widget?.model?.text_no_select_color)), + ), + )); + } + } else { + lists.add(Container( + height: 1, + )); + } + + return Column( + children: lists, + ); + } + + /// 获取筛选列表 + Widget _getSXWidget(SearchResultSortModel data) { + List lists = []; + List model; + int sort_list_length = data?.sort_list?.length ?? 0; + if (sort_list_length > 0) { + data.sort_list.forEach((item) { + if (item.type == 'popup-filter') { + model = item.select_list; + } + }); + + int length = model?.length ?? 0; + if (length > 0) { + for (int i = 0; i < model.length; i++) { + if (i == 0) { + lists.add(Container( + padding: const EdgeInsets.only(left: 12.5, right: 12.5, top: 10), + decoration: BoxDecoration(color: Colors.white, border: Border.all(width: 0, color: Colors.transparent)), + child: _getPriceBetweenWidget(), + )); + } + if (i == 1) { + lists.add(Container( + padding: const EdgeInsets.only(left: 12.5, right: 12.5, top: 13, bottom: 15), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(width: 0, color: Colors.transparent), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(12.5), + bottomRight: Radius.circular(12.5), + )), + child: _getStoryType())); + } + } + } else { + lists.add(Container()); + } + } else { + lists.add(Container()); + } + + return Column(children: lists); + } + + /// 价格区间 + Widget _getPriceBetweenWidget() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '价格区间(元)', + style: TextStyle(fontSize: 14, color: HexColor.fromHex('#999999')), + ), + Row( + children: [ + /// 最低价格 + _getCustomTextField(controller: _startPriceEditingController, focusNode: _startPriceNode, hintText: '最低价格', hintTextColor: '#999999'), + + /// 文字 【至】 + Container(margin: const EdgeInsets.symmetric(horizontal: 8), child: Text('至', style: TextStyle(color: HexColor.fromHex('#999999'), fontSize: 14))), + + /// 最高价格 + _getCustomTextField(controller: _endPriceEditingController, focusNode: _endPriceNode, hintTextColor: '#999999', hintText: '最高价格'), + ], + ), + ], + ); + } + + /// 只看自营 + Widget _getStoryType() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '商家类型', + style: TextStyle(color: HexColor.fromHex('#999999'), fontSize: 14), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 21, vertical: 8.5), + child: Text( + '只看自营', + style: TextStyle(color: HexColor.fromHex('#999999'), fontSize: 12), + ), + decoration: BoxDecoration( + border: Border.all(color: HexColor.fromHex('#E7E7E7'), width: 0.5), + borderRadius: BorderRadius.circular(5), + ), + ), + ], + ); + } + + /// 输入框 + Widget _getCustomTextField({TextEditingController controller, FocusNode focusNode, String hintText, String hintTextColor, double radius = 5}) { + return Container( + width: 90, + height: 33.5, + alignment: Alignment.center, + decoration: BoxDecoration( + color: HexColor.fromHex('#F5F5F5'), + borderRadius: BorderRadius.circular(radius), + ), + child: TextField( + controller: controller, + focusNode: focusNode, + decoration: InputDecoration( + filled: true, + fillColor: Colors.transparent, + border: InputBorder.none, + focusedBorder: InputBorder.none, + focusedErrorBorder: InputBorder.none, + errorBorder: InputBorder.none, + disabledBorder: InputBorder.none, + enabledBorder: InputBorder.none, + hintText: hintText, + hintStyle: TextStyle(color: HexColor.fromHex(hintTextColor), fontSize: 12)), + ), + ); + } + + /// 自定义widget + Widget _getCustomWidget(SearchResultItemSort model) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _clickSortType(model), + child: Container( + child: Row( + children: [ + Text(model?.name ?? '综合', + style: TextStyle( + color: HexColor.fromHex(model.isSelect ? widget?.model?.text_select_color ?? '#FF4242' : widget?.model?.text_no_select_color ?? '#333333'), fontSize: 14)), + const SizedBox(width: 4), + Container( + width: 12, + height: 12, + alignment: Alignment.center, + color: Colors.white, + child: CachedNetworkImage( + imageUrl: model?.icon ?? '', + width: 12, + ) + ) + ], + ), + ), + ); + } + + /// 排序widget + List _createSortListWidget(SearchResultSortModel model) { + List list = []; + int length = model?.sort_list?.length ?? 0; + if (length > 0) { + for (int i = 0; i < model.sort_list.length; i++) { + SearchResultItemSort item = model.sort_list[i]; + list.add(_getCustomWidget(item)); + } + } + return list; + } + + /// 切换icon + Widget _getChangeIconWidget(SearchResultSortModel model) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _changeColumn(), + child: Container( + height: 25, + width: 34, + margin: const EdgeInsets.only(left: 5), + padding: const EdgeInsets.only(left: 9), + decoration: BoxDecoration( + // color: Colors.yellowAccent, + border: Border( + left: BorderSide(color: HexColor.fromHex('#C9C5C5'), width: 0.5), + )), + alignment: Alignment.center, + child: CachedNetworkImage( + imageUrl: _isShowOneColumn ? model?.layout_icon_2 ?? '' : model?.layout_icon_1 ?? '', + width: 12, + ), + ), + ); + } +} diff --git a/lib/widgets/search_result/tarbar/model/search_result_tab_model.dart b/lib/widgets/search_result/tarbar/model/search_result_tab_model.dart new file mode 100644 index 0000000..3a7035f --- /dev/null +++ b/lib/widgets/search_result/tarbar/model/search_result_tab_model.dart @@ -0,0 +1,73 @@ +class SearchResultTabModel { + List search_icon_list; + String keywords; + + SearchResultTabModel({ + this.search_icon_list, + this.keywords, + }); + + factory SearchResultTabModel.fromJson(Map json) { + return SearchResultTabModel( + search_icon_list: json['search_icon_list'] != null ? (json['search_icon_list'] as List).map((i) => SearchResultTabItemModel.fromJson(i)).toList() : null, + keywords: json['keywords'], + ); + } + + Map toJson() { + final Map data = new Map(); + if (this.search_icon_list != null) { + data['search_icon_list'] = this.search_icon_list.map((v) => v.toJson()).toList(); + } + data['keywords'] = keywords; + return data; + } +} + +class SearchResultTabItemModel { + String icon; + String line_select_color; + String name; + String name_color; + String name_select_color; + String type; + String with_icon_color; + String skip_identifier; + + SearchResultTabItemModel({ + this.icon, + this.line_select_color, + this.name, + this.name_color, + this.name_select_color, + this.type, + this.with_icon_color, + this.skip_identifier, + }); + + factory SearchResultTabItemModel.fromJson(Map json) { + return SearchResultTabItemModel( + icon: json['icon'], + line_select_color: json['line_select_color'], + name: json['name'], + name_color: json['name_color'], + name_select_color: json['name_select_color'], + type: json['type'], + with_icon_color: json['with_icon_color'], + skip_identifier: json['skip_identifier'], + ); + } + + Map toJson() { + final Map data = new Map(); + data['icon'] = this.icon; + data['line_select_color'] = this.line_select_color; + data['name'] = this.name; + data['name_color'] = this.name_color; + data['name_select_color'] = this.name_select_color; + data['type'] = this.type; + data['with_icon_color'] = this.with_icon_color; + data['skip_identifier'] = this.skip_identifier; + return data; + } +} diff --git a/lib/widgets/search_result/tarbar/search_result_tab_creater.dart b/lib/widgets/search_result/tarbar/search_result_tab_creater.dart new file mode 100644 index 0000000..526c8df --- /dev/null +++ b/lib/widgets/search_result/tarbar/search_result_tab_creater.dart @@ -0,0 +1,23 @@ +import 'package:flutter/cupertino.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +import 'search_result_tab_widget.dart'; + +class SearchResultTabCreater extends WidgetCreater{ + + @override + List createWidgets(Map model) { + return [ + // SliverPersistentHeader( + // delegate: SliverTabBarDelegate( + // SearchTabWidget(model), + // ), + // ), + Expanded( + child: SearchResultTabWidget(model) + ), + ]; + } + + +} \ No newline at end of file diff --git a/lib/widgets/search_result/tarbar/search_result_tab_sk.dart b/lib/widgets/search_result/tarbar/search_result_tab_sk.dart new file mode 100644 index 0000000..2a262d2 --- /dev/null +++ b/lib/widgets/search_result/tarbar/search_result_tab_sk.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_comm/util/shimmer_util.dart'; + +class SearchResultTabSkeleton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + margin: const EdgeInsets.only(left: 12.5, right: 12.5, top: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ShimmerUtil.getShimmerWidget(width: 45, height: 15), + ShimmerUtil.getShimmerWidget(width: 45, height: 15), + ShimmerUtil.getShimmerWidget(width: 45, height: 15), + ShimmerUtil.getShimmerWidget(width: 45, height: 15), + ShimmerUtil.getShimmerWidget(width: 45, height: 15), + ], + ), + ); + } +} diff --git a/lib/widgets/search_result/tarbar/search_result_tab_widget.dart b/lib/widgets/search_result/tarbar/search_result_tab_widget.dart new file mode 100644 index 0000000..bdb461e --- /dev/null +++ b/lib/widgets/search_result/tarbar/search_result_tab_widget.dart @@ -0,0 +1,163 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:tab_indicator_styler/tab_indicator_styler.dart'; +import 'package:zhiying_base_widget/pages/search_think_page/model/search_think_model.dart'; +import 'package:zhiying_base_widget/widgets/home/home_quick_entry/cached_network_image_util.dart'; +import 'package:zhiying_base_widget/widgets/search/tabbar/search_tab_sk.dart'; +import 'package:zhiying_base_widget/widgets/search/widget/my_tab.dart'; +import 'package:zhiying_base_widget/widgets/search_result/sort/search_result_sort_widget.dart'; + +import 'package:zhiying_comm/zhiying_comm.dart'; + +import 'model/search_result_tab_model.dart'; + +class SearchResultTabWidget extends StatefulWidget { + final Map data; + SearchResultTabModel model; + + SearchResultTabWidget(this.data, {Key key}) : super(key: key) { + try { + model = SearchResultTabModel.fromJson(jsonDecode(data['data'])); + } catch (e) { + Logger.error(e.toString()); + } + } + + @override + _SearchResultTabWidgetState createState() => _SearchResultTabWidgetState(); +} + +class _SearchResultTabWidgetState extends State { + TabController _tabController; + + + /// 联想点击事件 + _onThinkItemClick(SearchThinkModel model){ + + } + + @override + void initState() { + _tabController = TabController(length: widget?.model?.search_icon_list?.length ?? 0, vsync: ScrollableState()); + super.initState(); + } + + @override + void dispose() { + _tabController?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return _getMainWidget(widget?.model); + } + + /// 获取主视图 + Widget _getMainWidget(SearchResultTabModel model) { + return Visibility( + replacement: SearchTabSkeleton(), + visible: !EmptyUtil.isEmpty(model), + child: _getTabar(model), + ); + } + + /// 获取TabBar + Widget _getTabar(SearchResultTabModel model) { + return Container( + margin: const EdgeInsets.only(/*left: 12.5, right: 12.5,*/ top: 5), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TabBar( + controller: _tabController, + isScrollable: true, + labelStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + unselectedLabelColor: HexColor.fromHex('#999999'), + labelColor: HexColor.fromHex('#FF4242'), + // indicatorSize: TabBarIndicatorSize.label, + indicator: MaterialIndicator( + height: 2.5, + topLeftRadius: 8, + topRightRadius: 8, + bottomLeftRadius: 8, + bottomRightRadius: 8, + color: HexColor.fromHex('#FF4242'), + horizontalPadding: 25, + ), + tabs: model.search_icon_list.map((item) { + return MyTab( + icon: CachedNetworkImage( + imageUrl: item?.with_icon_color ?? '', + width: 14, + ), + text: item.name, + ); + }).toList(), + ), + + // SearchResultSortWidget(null), + + Expanded( + child: _getStackWidget(model), + ), + ], + ), + ); + } + + + Widget _getStackWidget(SearchResultTabModel model){ + return Stack( + children: [ + + /// tabBraviwe + _getTabBarViewWidget(model), + + Visibility( + visible: true, + child: Container(), + ), + + ], + ); + } + + + /// 联想列表 + Widget _getThinkListWidget(List model){ + return ListView.builder(itemBuilder: (context, index){ + SearchThinkModel item = model[index]; + return GestureDetector( + onTap: ()=> _onThinkItemClick(item), + child: Container( + decoration: BoxDecoration( + border: Border(bottom: BorderSide(width: 0.5, color: HexColor.fromHex('#EEEEEE'))) + ), + padding: const EdgeInsets.only(top: 13, bottom: 13), + child: Text('${item?.keywords}', style: TextStyle( color: HexColor.fromHex('#333333'), fontSize: 14),), + ), + ); + }, + itemCount: model?.length ?? 0, + padding: const EdgeInsets.only(left: 12.5, right: 12.5), + shrinkWrap: true, + ); + } + + /// tabBraviwe + Widget _getTabBarViewWidget(SearchResultTabModel model){ + return TabBarView( + physics: NeverScrollableScrollPhysics(), + controller: _tabController, + children: model.search_icon_list.map((item) { + // TODO 这里需要和后台沟通改成页面的唯一标示 + return PageFactory.create('search_result_item', item.toJson()..['keywords']= widget?.model?.keywords ?? ''); + }).toList(), + ); + } + +}