@@ -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<GoodsDetailsContainer> { | |||
if (state is GoodsDetailsPageLoadedState) { | |||
return _getMainWidget(state?.model); | |||
} | |||
return _getMainWidget(null); | |||
return GoodsDetailsPageSkeleton(); | |||
}, | |||
), | |||
), | |||
@@ -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: <Widget>[ | |||
Column( | |||
crossAxisAlignment: CrossAxisAlignment.start, | |||
children: <Widget>[ | |||
/// 轮播图 | |||
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(), | |||
) | |||
], | |||
), | |||
); | |||
} | |||
} |
@@ -37,6 +37,12 @@ class SearchBloc extends Bloc<SearchEvent, SearchState> { | |||
/// 初始化 | |||
Stream<SearchState> _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()}'); | |||
@@ -5,7 +5,7 @@ class SearchRepository { | |||
/// 获取网络数据 | |||
Future<List<Map<String, dynamic>>> fetchInit(final Map<String, dynamic> 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<List<Map<String, dynamic>>> fetchCachedData(final Map<String, dynamic> model) {} | |||
Future<List<Map<String, dynamic>>> fetchCachedData(final Map<String, dynamic> 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<void> fetchSearchSubmit(final String model) async { | |||
@@ -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<SearchItemPageEvent, SearchItemPageState> { | |||
// SearchItemPageBloc() : super(SearchItemPageInitial()); | |||
SearchItemRepository repository; | |||
SearchItemPageBloc({@required this.repository}); | |||
@override | |||
// TODO: implement initialState | |||
SearchItemPageState get initialState => SearchItemPageInitial(); | |||
@override | |||
Stream<SearchItemPageState> mapEventToState( | |||
SearchItemPageEvent event, | |||
) async* { | |||
/// 初始化 | |||
if (event is SearchItemPageInitEvent) { | |||
yield* _mapInitEventToState(event); | |||
} | |||
} | |||
/// 初始化 | |||
Stream<SearchItemPageState> _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(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
part of 'search_item_page_bloc.dart'; | |||
abstract class SearchItemPageEvent extends Equatable { | |||
const SearchItemPageEvent(); | |||
} | |||
/// 初始化 | |||
class SearchItemPageInitEvent extends SearchItemPageEvent { | |||
final Map<String, dynamic> model; | |||
const SearchItemPageInitEvent({@required this.model}); | |||
@override | |||
List<Object> get props => [this.model]; | |||
} |
@@ -0,0 +1,26 @@ | |||
part of 'search_item_page_bloc.dart'; | |||
abstract class SearchItemPageState extends Equatable { | |||
const SearchItemPageState(); | |||
@override | |||
List<Object> get props => []; | |||
} | |||
class SearchItemPageInitial extends SearchItemPageState { | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 数据加载成功 | |||
class SearchItemLoadedState extends SearchItemPageState { | |||
List<Map<String, dynamic>> model; | |||
SearchItemLoadedState({this.model}); | |||
@override | |||
List<Object> get props => [this.model]; | |||
} | |||
/// 数据加载失败 | |||
class SearchItemErrorState extends SearchItemPageState {} |
@@ -0,0 +1,34 @@ | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class SearchItemRepository { | |||
List<Map<String, dynamic>> _pageData = []; | |||
/// 获取网络数据 | |||
Future<List<Map<String, dynamic>>> fetchInit(final Map<String, dynamic> 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<List<Map<String, dynamic>>> fetchCachedData(final Map<String, dynamic> 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; | |||
} | |||
} |
@@ -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<String, dynamic> data; | |||
const SearchItemPage(this.data); | |||
@override | |||
_SearchItemPageState createState() => _SearchItemPageState(); | |||
} | |||
class _SearchItemPageState extends State<SearchItemPage> with AutomaticKeepAliveClientMixin { | |||
@override | |||
bool get wantKeepAlive => true; | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocProvider<SearchItemPageBloc>( | |||
create: (_) => SearchItemPageBloc(repository: SearchItemRepository())..add(SearchItemPageInitEvent(model: widget?.data)), | |||
child: SearchItemPageContainer(), | |||
); | |||
} | |||
} | |||
class SearchItemPageContainer extends StatefulWidget { | |||
@override | |||
_SearchItemPageContainerState createState() => _SearchItemPageContainerState(); | |||
} | |||
class _SearchItemPageContainerState extends State<SearchItemPageContainer> { | |||
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<SearchItemPageBloc, SearchItemPageState>( | |||
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<Map<String, dynamic>> 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<Widget> _createContentWidget(List<Map<String, dynamic>> datas) { | |||
List<Widget> list = []; | |||
int length = datas?.length ?? 0; | |||
if (length > 0) { | |||
for (int i = 0; i < datas.length; i++) { | |||
WidgetModel item = WidgetModel.fromJson(Map<String, dynamic>.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; | |||
} | |||
} |
@@ -0,0 +1,59 @@ | |||
import 'dart:convert'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:shared_preferences/shared_preferences.dart'; | |||
class SearchTagNotifier with ChangeNotifier { | |||
///文本标签集合 | |||
List<String> _tagList = []; | |||
/// 保存历史标签的sp key | |||
static final String _SP_HOISTROY_KEY = 'hoistroyTag'; | |||
/// 最大存储条数 | |||
static final int _MAX_COUNT = 10; | |||
/// 初始化历史搜索标签 | |||
Future<List<String>> 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(); | |||
} | |||
} |
@@ -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<SearchBloc>( | |||
create: (_) => SearchBloc(repository: SearchRepository())..add(SearchInitEvent(model: data)), | |||
child: SearchPageContianer(), | |||
return MultiProvider( | |||
providers: [ | |||
ChangeNotifierProvider.value(value: SearchTagNotifier()) | |||
], | |||
child: MultiProvider( | |||
providers: [ | |||
/// 搜索页面的bloc | |||
BlocProvider<SearchBloc>( | |||
create: (_) => SearchBloc(repository: SearchRepository())..add(SearchInitEvent(model: data)), | |||
), | |||
/// 输入框联想的bloc | |||
BlocProvider<SearchThinkBloc>( | |||
create: (_)=> SearchThinkBloc(SearchThinkRepository()), | |||
), | |||
], | |||
child: SearchPageContianer(), | |||
), | |||
// child: BlocProvider<SearchBloc>( | |||
// create: (_) => SearchBloc(repository: SearchRepository())..add(SearchInitEvent(model: data)), | |||
// child: SearchPageContianer(), | |||
// ), | |||
); | |||
} | |||
} | |||
@@ -30,17 +55,15 @@ class SearchPageContianer extends StatefulWidget { | |||
class _SearchPageContianerState extends State<SearchPageContianer> { | |||
/// 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<SearchPageContianer> { | |||
Widget _getMainWidget(List<Map<String, dynamic>> datas) { | |||
return Scaffold( | |||
backgroundColor: Colors.white, | |||
body: CustomScrollView( | |||
controller: _controller, | |||
slivers: _createContent(context, datas ?? []), | |||
body: Column( | |||
children: _createContentWidget(datas), | |||
), | |||
); | |||
} | |||
List<Widget> _createContent(BuildContext context, List<Map<String, dynamic>> datas) { | |||
List<Widget> list = List(); | |||
List<Widget> _createContentWidget(List<Map<String, dynamic>> datas) { | |||
List<Widget> 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<String, dynamic>.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<String, dynamic>.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<Widget> _createContent(BuildContext context, List<Map<String, dynamic>> datas) { | |||
// List<Widget> 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<String, dynamic>.from(datas[i])); | |||
// print('item.modName ${item.modName}'); | |||
// list.addAll(WidgetFactory.create( | |||
// item.modName, | |||
// isSliver: true, | |||
// model: datas[i], | |||
// )); | |||
// } | |||
// | |||
// return list; | |||
// } | |||
} |
@@ -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<String, dynamic> data; | |||
const SearchPage2(this.data); | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocProvider<SearchBloc>( | |||
create: (_) => SearchBloc(repository: SearchRepository())..add(SearchInitEvent(model: data)), | |||
child: SearchPage2Container(), | |||
); | |||
} | |||
} | |||
class SearchPage2Container extends StatefulWidget { | |||
@override | |||
_SearchPage2ContainerState createState() => _SearchPage2ContainerState(); | |||
} | |||
class _SearchPage2ContainerState extends State<SearchPage2Container> { | |||
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<SearchBloc, SearchState>( | |||
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<Map<String, dynamic>> datas){ | |||
return Scaffold( | |||
backgroundColor: Colors.white, | |||
body: NestedScrollView( | |||
headerSliverBuilder: (context, bool){ | |||
return _createHeadWidget(context, datas); | |||
}, | |||
body: ListView( | |||
children: <Widget>[ | |||
Text('sss') | |||
], | |||
), | |||
), | |||
); | |||
} | |||
/// 头部视图 | |||
List<Widget> _createHeadWidget(BuildContext context, List<Map<String, dynamic>> datas){ | |||
List<Widget> 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<String, dynamic>.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; | |||
} |
@@ -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<String, dynamic> data; | |||
const SearchPage3(this.data); | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocProvider<SearchBloc>( | |||
create: (_) => SearchBloc(repository: SearchRepository())..add(SearchInitEvent(model: data)), | |||
child: SearchPage3Container(), | |||
); | |||
} | |||
} | |||
class SearchPage3Container extends StatefulWidget { | |||
@override | |||
_SearchPage3ContainerState createState() => _SearchPage3ContainerState(); | |||
} | |||
class _SearchPage3ContainerState extends State<SearchPage3Container> { | |||
@override | |||
Widget build(BuildContext context) { | |||
return MediaQuery.removePadding( | |||
removeTop: true, | |||
context: context, | |||
child: Container( | |||
width: double.infinity, | |||
child: BlocConsumer<SearchBloc, SearchState>( | |||
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<Map<String, dynamic>> datas){ | |||
return Scaffold( | |||
backgroundColor: Colors.white, | |||
body: NestedScrollView( | |||
headerSliverBuilder: (context, bool){ | |||
return _createHeadWidget(context, datas); | |||
}, | |||
body: Container( | |||
child: Text('testssfee'), | |||
), | |||
), | |||
); | |||
} | |||
/// 头部视图 | |||
List<Widget> _createHeadWidget(BuildContext context, List<Map<String, dynamic>> datas){ | |||
List<Widget> 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<String, dynamic>.from(datas[i])); | |||
print('item.modName ${item.modName}'); | |||
list.addAll(WidgetFactory.create( | |||
item.modName, | |||
isSliver: true, | |||
model: datas[i], | |||
)); | |||
} | |||
return list; | |||
} | |||
/// 子视图 | |||
List<Widget> _createContent(BuildContext context, List<Map<String, dynamic>> datas) { | |||
List<Widget> list = List(); | |||
/// datas.length - 1 为最后一个在底部 | |||
for (int i = 0; i < datas.length; i++) { | |||
WidgetModel item = WidgetModel.fromJson(Map<String, dynamic>.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; | |||
} | |||
} | |||
@@ -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<SearchResultEvent, SearchResultState> { | |||
// SearchResultBloc() : super(SearchResultInitial()); | |||
SearchResultRepository repository; | |||
SearchResultBloc({@required this.repository}); | |||
@override | |||
SearchResultState get initialState => SearchResultInitial(); | |||
@override | |||
Stream<SearchResultState> mapEventToState( | |||
SearchResultEvent event, | |||
) async* { | |||
if (event is SearchResultInitEvent) { | |||
yield* _mapInitEventToState(event); | |||
} | |||
} | |||
/// 初始化 | |||
Stream<SearchResultState> _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(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
part of 'search_result_bloc.dart'; | |||
abstract class SearchResultEvent extends Equatable { | |||
const SearchResultEvent(); | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 初始化数据 | |||
class SearchResultInitEvent extends SearchResultEvent { | |||
final Map<String, dynamic> model; | |||
const SearchResultInitEvent({@required this.model}); | |||
@override | |||
List<Object> get props => [this.model]; | |||
} |
@@ -0,0 +1,60 @@ | |||
import 'dart:convert'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class SearchResultRepository { | |||
List<Map<String, dynamic>> _pageData = []; | |||
/// 获取网络数据 | |||
Future<List<Map<String, dynamic>>> fetchInit(final Map<String, dynamic> 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<Map<String, dynamic>> modList = List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['mod_list']); | |||
modList.forEach((Map<String, dynamic> 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<List<Map<String, dynamic>>> fetchCachedData(final Map<String, dynamic> 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<Map<String, dynamic>> modList = List.from(result['mod_list']); | |||
List<Map<String, dynamic>> _pageData = []; | |||
modList.forEach((Map<String, dynamic> 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<void> fetchSearchSubmit(final String model) async { | |||
var result = await NetUtil.post(''); | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
part of 'search_result_bloc.dart'; | |||
abstract class SearchResultState extends Equatable { | |||
const SearchResultState(); | |||
@override | |||
List<Object> get props => []; | |||
} | |||
class SearchResultInitial extends SearchResultState { | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 数据加载完毕 | |||
class SearchResultLoadedState extends SearchResultState { | |||
List<Map<String, dynamic>> model; | |||
SearchResultLoadedState({@required this.model}); | |||
@override | |||
List<Object> get props => [this.model]; | |||
} | |||
/// 数据加载失败 | |||
class SearchResultErrorState extends SearchResultState {} |
@@ -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<SearchResultItemEvent, SearchResultItemState> { | |||
// SearchResultItemBloc() : super(SearchResultItemInitial()); | |||
SearchResultItemRepository repository; | |||
@override | |||
SearchResultItemState get initialState => SearchResultItemInitial(); | |||
SearchResultItemBloc({@required this.repository}); | |||
@override | |||
Stream<SearchResultItemState> mapEventToState( | |||
SearchResultItemEvent event, | |||
) async* { | |||
if (event is SearchResultItemInitEvent) { | |||
yield* _mapInitEventToState(event); | |||
} | |||
} | |||
/// 初始化 | |||
Stream<SearchResultItemState> _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(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
part of 'search_result_item_bloc.dart'; | |||
abstract class SearchResultItemEvent extends Equatable { | |||
const SearchResultItemEvent(); | |||
} | |||
/// 初始化 | |||
class SearchResultItemInitEvent extends SearchResultItemEvent{ | |||
final Map<String, dynamic> model; | |||
const SearchResultItemInitEvent({@required this.model}); | |||
@override | |||
List<Object> get props => [this.model]; | |||
} |
@@ -0,0 +1,55 @@ | |||
import 'dart:convert'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class SearchResultItemRepository { | |||
List<Map<String, dynamic>> _pageData; | |||
/// 初始化 | |||
Future<List<Map<String, dynamic>>> fetchInitData(final Map<String, dynamic> 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<Map<String, dynamic>> modList = List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['mod_list']); | |||
modList.forEach((Map<String, dynamic> 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<List<Map<String, dynamic>>> fetchCacheData(final Map<String, dynamic> 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<Map<String, dynamic>> modList = List.from(result['mod_list']); | |||
List<Map<String, dynamic>> _pageData = []; | |||
modList.forEach((Map<String, dynamic> item) { | |||
var data = jsonDecode(item['data']); | |||
data['keywords'] = keywords; | |||
item['data'] = jsonEncode(data); | |||
_pageData.add(item); | |||
}); | |||
return _pageData; | |||
} | |||
} catch (e) {} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
part of 'search_result_item_bloc.dart'; | |||
abstract class SearchResultItemState extends Equatable { | |||
const SearchResultItemState(); | |||
@override | |||
List<Object> get props => []; | |||
} | |||
class SearchResultItemInitial extends SearchResultItemState {} | |||
/// 数据加载完毕 | |||
class SearchResultItemLoadedState extends SearchResultItemState { | |||
List<Map<String, dynamic>> model; | |||
SearchResultItemLoadedState({@required this.model}); | |||
@override | |||
List<Object> get props => [this.model]; | |||
} | |||
/// 数据加载失败 | |||
class SearchResultItemErrorState extends SearchResultItemState {} |
@@ -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(); | |||
} | |||
} |
@@ -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<String, dynamic> data; | |||
const SearchResultItemPage(this.data); | |||
@override | |||
Widget build(BuildContext context) { | |||
return MultiProvider( | |||
providers: [ | |||
ChangeNotifierProvider.value(value: SearchResultItemPageNotifier()), | |||
], | |||
// child: BlocProvider<SearchResultItemBloc>( | |||
// create: (_) => SearchResultItemBloc(repository: SearchResultItemRepository())..add(SearchResultItemInitEvent(model: data)), | |||
// child: SearchResultItemPageContianer(), | |||
// ), | |||
child: MultiBlocProvider( | |||
providers: [ | |||
/// 视图BLOC | |||
BlocProvider<SearchResultItemBloc>( | |||
create: (_) => SearchResultItemBloc(repository: SearchResultItemRepository())..add(SearchResultItemInitEvent(model: data)), | |||
), | |||
/// 商品列表Bloc | |||
BlocProvider<SearchResultGoodsListBloc>( | |||
create: (_) => SearchResultGoodsListBloc(repository: SearchResultGoodsListRepository(reqData: data)), | |||
), | |||
], | |||
child: SearchResultItemPageContianer(), | |||
), | |||
); | |||
} | |||
} | |||
class SearchResultItemPageContianer extends StatefulWidget { | |||
@override | |||
_SearchResultItemPageContianerState createState() => _SearchResultItemPageContianerState(); | |||
} | |||
class _SearchResultItemPageContianerState extends State<SearchResultItemPageContianer> 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<SearchResultItemPageNotifier>(context, listen: false).loadMore(); | |||
} else if (_controller.offset < _controller.position.maxScrollExtent && _isEnded) { | |||
_isEnded = false; | |||
Provider.of<SearchResultItemPageNotifier>(context, listen: false).reset(); | |||
} | |||
}); | |||
super.initState(); | |||
} | |||
@override | |||
void dispose() { | |||
_refreshController?.dispose(); | |||
_controller?.dispose(); | |||
super.dispose(); | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocConsumer<SearchResultItemBloc, SearchResultItemState>( | |||
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<SearchResultGoodsListBloc>(context).add(SearchResultGoodsListOnLoadEvent()); | |||
_refreshController.loadComplete(); | |||
} | |||
/// 下拉刷新 | |||
void _onRefresh() async { | |||
BlocProvider.of<SearchResultGoodsListBloc>(context).add(SearchResultGoodsListOnRefreshEvent()); | |||
_refreshController.refreshCompleted(resetFooterState: true); | |||
} | |||
/// 获取主视图 | |||
Widget _getMainWidget(List<Map<String, dynamic>> datas) { | |||
return Scaffold( | |||
backgroundColor: HexColor.fromHex('#F9F9F9'), | |||
body: Stack( | |||
children: <Widget>[ | |||
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<Map<String, dynamic>> datas) { | |||
int length = datas?.length ?? 0; | |||
if (length == 0) { | |||
return Container(); | |||
} else { | |||
WidgetModel item = WidgetModel.fromJson(Map<String, dynamic>.from(datas[0])); | |||
List<Widget> 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<Widget> _createContentWidget(List<Map<String, dynamic>> datas) { | |||
List<Widget> list = []; | |||
int length = datas?.length ?? 0; | |||
if (length > 0) { | |||
/// 去除筛选栏, 从1开始 | |||
for (int i = 1; i < datas.length; i++) { | |||
WidgetModel item = WidgetModel.fromJson(Map<String, dynamic>.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; | |||
} | |||
} |
@@ -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<String, dynamic> data; | |||
const SearchResultPage(this.data); | |||
@override | |||
Widget build(BuildContext context) { | |||
// return BlocProvider<SearchResultBloc>( | |||
// create: (_) => SearchResultBloc(repository: SearchResultRepository())..add(SearchResultInitEvent(model: data)), | |||
// child: SearchResultContianer(), | |||
// ); | |||
return MultiBlocProvider( | |||
providers: [ | |||
/// 页面的数据 | |||
BlocProvider<SearchResultBloc>( | |||
create: (_) => SearchResultBloc(repository: SearchResultRepository())..add(SearchResultInitEvent(model: data)), | |||
), | |||
/// 输入框联想的bloc | |||
BlocProvider<SearchThinkBloc>( | |||
create: (_)=> SearchThinkBloc(SearchThinkRepository()), | |||
), | |||
], | |||
child: SearchResultContianer(), | |||
); | |||
} | |||
} | |||
class SearchResultContianer extends StatefulWidget { | |||
@override | |||
_SearchResultContianerState createState() => _SearchResultContianerState(); | |||
} | |||
class _SearchResultContianerState extends State<SearchResultContianer> { | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocConsumer<SearchResultBloc, SearchResultState>( | |||
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<Map<String, dynamic>> data) { | |||
return Scaffold( | |||
backgroundColor: Colors.white, | |||
body: Column( | |||
crossAxisAlignment: CrossAxisAlignment.start, | |||
mainAxisAlignment: MainAxisAlignment.start, | |||
children: _createContentWidget(data), | |||
), | |||
); | |||
} | |||
/// 创建视图 | |||
List<Widget> _createContentWidget(List<Map<String, dynamic>> datas) { | |||
int length = datas?.length ?? 0; | |||
List<Widget> list = []; | |||
if (length > 0) { | |||
for (int i = 0; i < length; i++) { | |||
WidgetModel item = WidgetModel.fromJson(Map<String, dynamic>.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; | |||
} | |||
} |
@@ -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<SearchThinkEvent, SearchThinkState> { | |||
// SearchThinkBloc() : super(SearchThinkInitial()); | |||
@override | |||
SearchThinkState get initialState => SearchThinkInitial(); | |||
SearchThinkRepository repository; | |||
SearchThinkBloc(this.repository); | |||
@override | |||
Stream<SearchThinkState> mapEventToState(SearchThinkEvent event,) async* { | |||
/// 关键字改变的方法 | |||
if (event is SearchThinkKeyWrodsChangeEvent) { | |||
yield* _mapKeyWordsChangeToState(event); | |||
} | |||
/// 修改渠道关键字 | |||
if (event is SearchThinkChangeTypeEvent) { | |||
_mapChangeTypeToState(event); | |||
} | |||
/// 显示原本的视图 | |||
if (event is SearchThinkShowBaseViewEvent) { | |||
yield* _mapShowBaseViewEventToState(); | |||
} | |||
} | |||
/// 关键字改变的方法 | |||
Stream<SearchThinkState> _mapKeyWordsChangeToState(SearchThinkKeyWrodsChangeEvent event) async* { | |||
var result = await repository.fetchkeyWordsChange(event.keywords); | |||
if (!EmptyUtil.isEmpty(result)) { | |||
yield SearchThinkLoadedState(result); | |||
} else { | |||
yield SearchThinkErrorState(); | |||
} | |||
} | |||
/// 显示原本的视图 | |||
Stream<SearchThinkState> _mapShowBaseViewEventToState() async* { | |||
yield SearchThinkShowBaseViewStatte(); | |||
} | |||
/// 修改商品电商类型 | |||
_mapChangeTypeToState(SearchThinkChangeTypeEvent event) { | |||
repository.changeType(event.type); | |||
} | |||
} |
@@ -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<Object> get props => [this.keywords]; | |||
} | |||
/// 修改电商类型 | |||
class SearchThinkChangeTypeEvent extends SearchThinkEvent{ | |||
final String type; | |||
SearchThinkChangeTypeEvent(this.type); | |||
@override | |||
List<Object> get props => [this.props]; | |||
} | |||
/// 显示原本视图 | |||
class SearchThinkShowBaseViewEvent extends SearchThinkEvent{ | |||
@override | |||
List<Object> get props => []; | |||
} |
@@ -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<List<SearchThinkModel>> 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<String> data = List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||
List<SearchThinkModel> 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'); | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
part of 'search_think_bloc.dart'; | |||
abstract class SearchThinkState extends Equatable { | |||
const SearchThinkState(); | |||
} | |||
class SearchThinkInitial extends SearchThinkState { | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 数据加载完毕 | |||
class SearchThinkLoadedState extends SearchThinkState{ | |||
List<SearchThinkModel> model; | |||
SearchThinkLoadedState(this.model); | |||
@override | |||
List<Object> get props => [this.model]; | |||
} | |||
/// 显示本来的视图 | |||
class SearchThinkShowBaseViewStatte extends SearchThinkState{ | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 数据加载出错 | |||
class SearchThinkErrorState extends SearchThinkState{ | |||
@override | |||
List<Object> get props => []; | |||
} |
@@ -0,0 +1,20 @@ | |||
class SearchThinkModel { | |||
String type; | |||
String keywords; | |||
SearchThinkModel({this.type, this.keywords}); | |||
factory SearchThinkModel.fromJson(Map<String, dynamic> json) { | |||
return SearchThinkModel( | |||
type: json['type'], | |||
keywords: json['keywords'], | |||
); | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
data['type'] = this.type; | |||
data['keywords'] = this.keywords; | |||
return data; | |||
} | |||
} |
@@ -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<String, dynamic> data; | |||
SearchThinkPage(this.data, {Key key}) : super(key: key) { | |||
try {} catch (_) {} | |||
} | |||
@override | |||
_SearchThinkPageState createState() => _SearchThinkPageState(); | |||
} | |||
class _SearchThinkPageState extends State<SearchThinkPage> { | |||
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: <Widget>[ | |||
/// 标题 | |||
_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: <Widget>[ | |||
/// 输入框 | |||
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, | |||
), | |||
), | |||
); | |||
} | |||
} |
@@ -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( | |||
@@ -24,8 +24,9 @@ class GoodsDetailsImgRepository { | |||
try { | |||
if(!EmptyUtil.isEmpty(_model)) { | |||
List<String> 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){ | |||
@@ -26,6 +26,17 @@ class GoodsDetailsImgModel { | |||
); | |||
} | |||
GoodsDetailsImgModel copyWith(List<String> newImgs){ | |||
return GoodsDetailsImgModel( | |||
image_detail_list: newImgs, | |||
title: title, | |||
title_color: title_color, | |||
provider: provider, | |||
good_id: good_id, | |||
icon: icon, | |||
); | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
data['title'] = this.title; | |||
@@ -40,7 +40,13 @@ class GooddsDetailsFooterContainer extends StatefulWidget { | |||
class _GooddsDetailsFooterContainerState | |||
extends State<GooddsDetailsFooterContainer> { | |||
/// 打开首页 | |||
void _openHome() {} | |||
void _openHome() { | |||
Navigator.pushAndRemoveUntil( | |||
context, | |||
MaterialPageRoute(builder: (BuildContext context) => PageFactory.create('homePage', null)), | |||
(Route<dynamic> route) => false, | |||
); | |||
} | |||
/// 收藏 | |||
void _collectOnClick() {} | |||
@@ -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<Object> get props => [this.goodId, this.provider, this.providerName]; | |||
} | |||
@@ -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 { | |||
@@ -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<Widget> 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), | |||
), | |||
)); | |||
@@ -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<String, dynamic> data; | |||
const HistoryTagWidget(this.data); | |||
@override | |||
_HistoryTagWidgetState createState() => _HistoryTagWidgetState(); | |||
} | |||
class _HistoryTagWidgetState extends State<HistoryTagWidget> { | |||
///文本标签集合 | |||
List<String> _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: <Widget>[ | |||
/// 标题 | |||
SearchTitleWidget(titleText: '历史搜索', titleTextColor: '#333333', iconUrl: '', callback: () => _clearTag()), | |||
const SizedBox( | |||
height: 10, | |||
), | |||
/// 历史标签 | |||
_getHistoryWarp(), | |||
], | |||
), | |||
); | |||
} | |||
/// 历史搜索的标签 | |||
Widget _getHistoryWarp() { | |||
List<Widget> 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(); | |||
} | |||
} |
@@ -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<String, dynamic> json) { | |||
return SearchHistoryModel( | |||
clear_history_icon: json['clear_history_icon'], | |||
history_bg_color: json['history_bg_color'], | |||
history_tilte: json['history_tilte'], | |||
); | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
data['clear_history_icon'] = this.clear_history_icon; | |||
data['history_bg_color'] = this.history_bg_color; | |||
data['history_tilte'] = this.history_tilte; | |||
return data; | |||
} | |||
} |
@@ -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<String, dynamic> 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<SearchHistoryTagWidget> { | |||
///文本标签集合 | |||
List<String> _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<SearchTagNotifier>(context, listen: false).clear(); | |||
setState(() { | |||
}); | |||
} | |||
@override | |||
void initState() { | |||
_initHistoryTag(); | |||
super.initState(); | |||
} | |||
_initHistoryTag() async { | |||
_tagList = await Provider.of<SearchTagNotifier>(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: <Widget>[ | |||
/// 标题 | |||
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<Widget> 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(); | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
class SearchHotTagModel { | |||
String hot_title; | |||
String hot_title_color; | |||
List<SearchHostTagItemModel> search_hosts; | |||
SearchHotTagModel({this.hot_title, this.hot_title_color, this.search_hosts}); | |||
factory SearchHotTagModel.fromJson(Map<String, dynamic> 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<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
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<String, dynamic> 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<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
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; | |||
} | |||
} |
@@ -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<String, dynamic> 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<SearchTagNotifier>(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: <Widget>[ | |||
/// 标题 | |||
_getTitleWidget(model), | |||
const SizedBox(height: 13), | |||
_getTagWidget(context, model), | |||
], | |||
), | |||
); | |||
} | |||
/// 标签widget | |||
Widget _getTagWidget(BuildContext context, SearchHotTagModel model) { | |||
List<Widget> 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', | |||
); | |||
} | |||
} |
@@ -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<SearchInputWidget> { | |||
/// 点击搜索按钮 | |||
void _onSearchButtomClick() {} | |||
void _onSearchButtomClick() async { | |||
String content = _editingController?.text?.toString()?.trim() ?? ''; | |||
/// TODO 保存历史标签 | |||
if (!EmptyUtil.isEmpty(content)) { | |||
await Provider.of<SearchTagNotifier>(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<SearchThinkBloc>(context).add(SearchThinkKeyWrodsChangeEvent(text)); | |||
}else{ | |||
/// 输入框为空的时候,隐藏联想视图,显示原本的视图 | |||
BlocProvider.of<SearchThinkBloc>(context).add(SearchThinkShowBaseViewEvent()); | |||
} | |||
} | |||
FocusNode _focusNode; | |||
TextEditingController _editingController; | |||
@@ -65,32 +100,63 @@ class _SearchInputWidgetState extends State<SearchInputWidget> { | |||
/// 获取主视图 | |||
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: <Widget>[ | |||
/// 搜索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: <Widget>[ | |||
/// 搜索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<SearchInputWidget> { | |||
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<SearchInputWidget> { | |||
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<SearchInputWidget> { | |||
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), | |||
), | |||
), | |||
@@ -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<String, dynamic> 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; | |||
} | |||
} |
@@ -13,12 +13,12 @@ class SearcchTabCreater extends WidgetCreater{ | |||
// SearchTabWidget(model), | |||
// ), | |||
// ), | |||
SearchTabWidget(model), | |||
Expanded(child: SearchTabWidget(model)), | |||
]; | |||
} | |||
@override | |||
bool isSliverChild() { | |||
return true; | |||
return false; | |||
} | |||
} |
@@ -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<String, dynamic> data; | |||
SearchTabModel model; | |||
@@ -29,12 +33,25 @@ class SearchTabWidget extends StatefulWidget { | |||
class _SearchTabWidgetState extends State<SearchTabWidget> { | |||
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<SearchThinkBloc>(context).add(SearchThinkChangeTypeEvent(type)); | |||
} | |||
}); | |||
super.initState(); | |||
} | |||
@@ -62,44 +79,110 @@ class _SearchTabWidgetState extends State<SearchTabWidget> { | |||
/// 获取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: <Widget>[ | |||
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: <Widget>[ | |||
_getTabBarView(model), | |||
BlocConsumer<SearchThinkBloc, SearchThinkState>( | |||
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<SearchThinkModel> 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, | |||
), | |||
); | |||
} | |||
} | |||
} |
@@ -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. | |||
/// | |||
@@ -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<TextTagWidget> { | |||
@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(), | |||
); | |||
} | |||
@@ -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<SearchResultGoodsListEvent, SearchResultGoodsListState> { | |||
// SearchResultGoodsListBloc() : super(SearchResultGoodsListInitial()); | |||
SearchResultGoodsListRepository repository; | |||
SearchResultGoodsListBloc({@required this.repository}); | |||
@override | |||
SearchResultGoodsListState get initialState => SearchResultGoodsListInitial(); | |||
@override | |||
Stream<SearchResultGoodsListState> 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<SearchResultGoodsListState> _mapInitEventToState(SearchResultGoodsListInitEvent event) async* { | |||
var result = await repository.fetchInitData(); | |||
if (!EmptyUtil.isEmpty(result)) { | |||
yield SearchResultGoodsListLoadedState(model: result); | |||
} else { | |||
yield SearchResultGoodsListErrorState(); | |||
} | |||
} | |||
/// 下拉刷新 | |||
Stream<SearchResultGoodsListState> _mapRefreshToState(SearchResultGoodsListOnRefreshEvent event) async* { | |||
var result = await repository.fetchRefreshData(); | |||
if (!EmptyUtil.isEmpty(result)) { | |||
yield SearchResultGoodsListLoadedState(model: result); | |||
} else { | |||
yield SearchResultGoodsListErrorState(); | |||
} | |||
} | |||
/// 上拉更多 | |||
Stream<SearchResultGoodsListState> _mapOnLoadToState(SearchResultGoodsListOnLoadEvent event, SearchResultGoodsListLoadedState state) async* { | |||
var result = await repository.fetchLoadData(); | |||
if (!EmptyUtil.isEmpty(result)) { | |||
yield SearchResultGoodsListLoadedState(model: result); | |||
} else { | |||
yield SearchResultGoodsListErrorState(); | |||
} | |||
} | |||
/// 修改样式 | |||
Stream<SearchResultGoodsListState> _mapChangeStyleToState() async* { | |||
var result = repository.fetchChangeStyle(); | |||
if (!EmptyUtil.isEmpty(result)) { | |||
yield SearchResultGoodsListChangeStyleState(result['style'], result['datas']); | |||
} else { | |||
yield SearchResultGoodsListErrorState(); | |||
} | |||
} | |||
/// 关键字搜索 | |||
Stream<SearchResultGoodsListState> _mapSubmitToState(SearchResultGoodsListSubmitEvent event) async* { | |||
var result = await repository.fetchSearchSubmit(event.keywords); | |||
if (!EmptyUtil.isEmpty(result)) { | |||
yield SearchResultGoodsListLoadedState(model: result); | |||
} else { | |||
yield SearchResultGoodsListErrorState(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
part of 'search_result_goods_list_bloc.dart'; | |||
abstract class SearchResultGoodsListEvent extends Equatable { | |||
const SearchResultGoodsListEvent(); | |||
@override | |||
List<Object> 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); | |||
} |
@@ -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<HomeGoodsModel> _oldDatas = []; | |||
/// 显示样式 | |||
bool _isShowOneColumn = true; | |||
/// 查询数据参数 | |||
Map<String, dynamic> reqData = {}; | |||
/// 初始化 | |||
Future<List<HomeGoodsModel>> fetchInitData() async { | |||
_currentPage = 1; | |||
_hasNomore = true; | |||
return _baseInitData(true, reqData); | |||
} | |||
/// 下拉刷新 | |||
Future<List<HomeGoodsModel>> fetchRefreshData() async { | |||
_currentPage = 1; | |||
_hasNomore = true; | |||
return _baseInitData(true,reqData); | |||
} | |||
/// 上拉更多 | |||
Future<List<HomeGoodsModel>> fetchLoadData() async { | |||
/// 只有更多数据的时候才进行加载 | |||
if (_hasNomore) { | |||
return _baseInitData(false, reqData); | |||
} else { | |||
return null; | |||
} | |||
} | |||
/// 修改显示样式 | |||
Map<String, dynamic> fetchChangeStyle() { | |||
_isShowOneColumn = !_isShowOneColumn; | |||
return {'style': _isShowOneColumn, 'datas': _oldDatas}; | |||
} | |||
/// 输入框修改关键字 | |||
Future<List<HomeGoodsModel>> fetchSearchSubmit(String keywords) async{ | |||
if(!EmptyUtil.isEmpty(keywords)) { | |||
reqData['reqData'] = keywords; | |||
_currentPage = 1; | |||
_hasNomore =true; | |||
return _baseInitData(true, reqData); | |||
} | |||
return null; | |||
} | |||
/// 查询 | |||
Future<List<HomeGoodsModel>> _baseInitData(bool refresh,final Map<String, dynamic> 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<HomeGoodsModel> newData = []; | |||
newData.addAll(_oldDatas); | |||
return newData; | |||
} | |||
} catch (e) { | |||
Logger.error(e); | |||
} | |||
_hasNomore = false; | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
part of 'search_result_goods_list_bloc.dart'; | |||
abstract class SearchResultGoodsListState extends Equatable { | |||
const SearchResultGoodsListState(); | |||
@override | |||
List<Object> get props => []; | |||
} | |||
class SearchResultGoodsListInitial extends SearchResultGoodsListState { | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 数据加载完毕 | |||
class SearchResultGoodsListLoadedState extends SearchResultGoodsListState { | |||
List<HomeGoodsModel> model; | |||
SearchResultGoodsListLoadedState({@required this.model}); | |||
SearchResultGoodsListLoadedState copyWith(List<HomeGoodsModel> newData) { | |||
return SearchResultGoodsListLoadedState(model: newData ?? model); | |||
} | |||
@override | |||
List<Object> get props => [this.model]; | |||
} | |||
/// 数据加载出错 | |||
class SearchResultGoodsListErrorState extends SearchResultGoodsListState {} | |||
/// 切换样式成功 | |||
class SearchResultGoodsListChangeStyleState extends SearchResultGoodsListState { | |||
bool isShowOneColumn = true; | |||
List<HomeGoodsModel> model; | |||
SearchResultGoodsListChangeStyleState(this.isShowOneColumn, this.model); | |||
@override | |||
List<Object> get props => [this.isShowOneColumn]; | |||
} |
@@ -1,9 +1,9 @@ | |||
import 'package:flutter/material.dart'; | |||
/// | |||
/// 搜索结果页面 | |||
/// 商品列表的骨架图 | |||
/// | |||
class SreachResultPage extends StatelessWidget { | |||
class SearchResultGoodsListSkeleton extends StatelessWidget { | |||
@override | |||
Widget build(BuildContext context) { | |||
return Container(); |
@@ -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<String, dynamic> data; | |||
HomeGoodsStyleModel style; | |||
SearchResultGoodsListWidget(this.data) { | |||
try { | |||
style = HomeGoodsStyleModel.fromJson(jsonDecode(data['data'])); | |||
} catch (e) {} | |||
} | |||
@override | |||
_SearchResultGoodsListWidgetState createState() => _SearchResultGoodsListWidgetState(); | |||
} | |||
class _SearchResultGoodsListWidgetState extends State<SearchResultGoodsListWidget> { | |||
@override | |||
Widget build(BuildContext context) { | |||
// return BlocProvider<SearchResultGoodsListBloc>( | |||
// create: (_) => SearchResultGoodsListBloc(repository: SearchResultGoodsListRepository())..add(SearchResultGoodsListInitEvent(model: widget?.data)), | |||
// child: SearchResultGoodsListWidgetContainer(), | |||
// ); | |||
return SearchResultGoodsListWidgetContainer(widget?.data, widget?.style); | |||
} | |||
} | |||
class SearchResultGoodsListWidgetContainer extends StatefulWidget { | |||
final Map<String, dynamic> data; | |||
final HomeGoodsStyleModel style; | |||
const SearchResultGoodsListWidgetContainer(this.data, this.style); | |||
@override | |||
_SearchResultGoodsListWidgetContainerState createState() => _SearchResultGoodsListWidgetContainerState(); | |||
} | |||
/// 商品列表 | |||
class _SearchResultGoodsListWidgetContainerState extends State<SearchResultGoodsListWidgetContainer> { | |||
/// 显示样式 | |||
bool _isShowOneColumn = true; | |||
/// 下拉刷新 | |||
void _onRefersh() async { | |||
BlocProvider.of<SearchResultGoodsListBloc>(context).add(SearchResultGoodsListOnRefreshEvent()); | |||
} | |||
/// 上拉更多 | |||
void _onLoad() async { | |||
BlocProvider.of<SearchResultGoodsListBloc>(context).add(SearchResultGoodsListOnLoadEvent()); | |||
} | |||
/// 跳转商品详情 | |||
void _openGoodsDetailsPage() { | |||
Navigator.push(context, MaterialPageRoute(builder: (_) => GoodsDetailsPage(null))); | |||
} | |||
@override | |||
void initState() { | |||
/// 初始化 | |||
BlocProvider.of<SearchResultGoodsListBloc>(context).add(SearchResultGoodsListInitEvent()); | |||
super.initState(); | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocConsumer<SearchResultGoodsListBloc, SearchResultGoodsListState>( | |||
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<HomeGoodsModel> 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<HomeGoodsModel> 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<HomeGoodsModel> goods, HomeGoodsStyleModel styleModel) { | |||
// int column = 2; | |||
// int count = (goods.length / column).ceil(); | |||
// | |||
// return Container( | |||
// child: Text('22222222'), | |||
// ); | |||
// } | |||
} |
@@ -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<String, dynamic> 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<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
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; | |||
} | |||
} |
@@ -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<String, dynamic> 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<SearchResultInputWidget> { | |||
TextEditingController _textEditingController; | |||
FocusNode _focusNode; | |||
/// 返回事件 | |||
void _openPop() { | |||
Navigator.maybePop(context); | |||
} | |||
/// 搜索事件 | |||
void _onSearchButtomClick() { | |||
print('搜索商品'); | |||
String keywords = _textEditingController?.text?.toString()?.trim(); | |||
if(!EmptyUtil.isEmpty(keywords)){ | |||
// BlocProvider.of<SearchResultGoodsListBloc>(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: <Widget>[ | |||
/// 返回键 | |||
_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), | |||
), | |||
), | |||
), | |||
); | |||
} | |||
} |
@@ -0,0 +1,126 @@ | |||
class SearchResultSortModel { | |||
String coupin_icon; | |||
String coupin_icon_with_color; | |||
String layout_icon_1; | |||
String layout_icon_2; | |||
List<SearchResultItemSort> 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<String, dynamic> 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<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
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<SearchResultSortSelect> 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<String, dynamic> 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<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
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<String, dynamic> json) { | |||
return SearchResultSortSelect( | |||
name: json['name'], | |||
query_args: json['query_args'], | |||
); | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
data['name'] = this.name; | |||
data['query_args'] = this.query_args; | |||
return data; | |||
} | |||
} |
@@ -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<String, dynamic> 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<SearchResultSortWidget> { | |||
TextEditingController _startPriceEditingController; | |||
TextEditingController _endPriceEditingController; | |||
FocusNode _startPriceNode; | |||
FocusNode _endPriceNode; | |||
// 是否展开综合 | |||
bool _isShowZh = false; | |||
// 切换显示样式 | |||
bool _isShowOneColumn = true; | |||
// 是否展开下拉选项 | |||
bool _expand = false; | |||
/// 切换类型 | |||
void _changeColumn() { | |||
setState(() => _isShowOneColumn = !_isShowOneColumn); | |||
BlocProvider.of<SearchResultGoodsListBloc>(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: <Widget>[ | |||
/// 筛选栏 | |||
Container( | |||
height: 41, | |||
width: double.infinity, | |||
color: Colors.white, | |||
padding: const EdgeInsets.only(left: 12.5, right: 12.5), | |||
child: Row( | |||
children: <Widget>[ | |||
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<SearchResultSortSelect> model; | |||
List<Widget> 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<Widget> lists = []; | |||
List<SearchResultSortSelect> 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: <Widget>[ | |||
Text( | |||
'价格区间(元)', | |||
style: TextStyle(fontSize: 14, color: HexColor.fromHex('#999999')), | |||
), | |||
Row( | |||
children: <Widget>[ | |||
/// 最低价格 | |||
_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: <Widget>[ | |||
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: <Widget>[ | |||
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<Widget> _createSortListWidget(SearchResultSortModel model) { | |||
List<Widget> 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, | |||
), | |||
), | |||
); | |||
} | |||
} |
@@ -0,0 +1,73 @@ | |||
class SearchResultTabModel { | |||
List<SearchResultTabItemModel> search_icon_list; | |||
String keywords; | |||
SearchResultTabModel({ | |||
this.search_icon_list, | |||
this.keywords, | |||
}); | |||
factory SearchResultTabModel.fromJson(Map<String, dynamic> 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<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
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<String, dynamic> 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<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
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; | |||
} | |||
} |
@@ -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<Widget> createWidgets(Map<String, dynamic> model) { | |||
return [ | |||
// SliverPersistentHeader( | |||
// delegate: SliverTabBarDelegate( | |||
// SearchTabWidget(model), | |||
// ), | |||
// ), | |||
Expanded( | |||
child: SearchResultTabWidget(model) | |||
), | |||
]; | |||
} | |||
} |
@@ -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: <Widget>[ | |||
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), | |||
], | |||
), | |||
); | |||
} | |||
} |
@@ -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<String, dynamic> 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<SearchResultTabWidget> { | |||
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: <Widget>[ | |||
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: <Widget>[ | |||
/// tabBraviwe | |||
_getTabBarViewWidget(model), | |||
Visibility( | |||
visible: true, | |||
child: Container(), | |||
), | |||
], | |||
); | |||
} | |||
/// 联想列表 | |||
Widget _getThinkListWidget(List<SearchThinkModel> 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(), | |||
); | |||
} | |||
} |