@@ -55,8 +55,7 @@ class GoodsDetailsContainer extends StatefulWidget { | |||||
class _GoodsDetailsContainerState extends State<GoodsDetailsContainer> { | class _GoodsDetailsContainerState extends State<GoodsDetailsContainer> { | ||||
bool _isEnded = false; | bool _isEnded = false; | ||||
ScrollController _controller = ScrollController(); | ScrollController _controller = ScrollController(); | ||||
RefreshController _refreshController = | |||||
RefreshController(initialRefresh: false); | |||||
RefreshController _refreshController = RefreshController(initialRefresh: false); | |||||
void _onLoading() async { | void _onLoading() async { | ||||
// await Future.delayed(Duration(milliseconds: 1000)); | // await Future.delayed(Duration(milliseconds: 1000)); | ||||
@@ -0,0 +1,54 @@ | |||||
import 'dart:async'; | |||||
import 'package:bloc/bloc.dart'; | |||||
import 'package:equatable/equatable.dart'; | |||||
import 'package:flutter/cupertino.dart'; | |||||
import 'search_repository.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
part 'search_event.dart'; | |||||
part 'search_state.dart'; | |||||
class SearchBloc extends Bloc<SearchEvent, SearchState> { | |||||
// SreachBloc() : super(SreachInitial()); | |||||
SearchRepository repository; | |||||
SearchBloc({this.repository}); | |||||
@override | |||||
SearchState get initialState => SearchInitial(); | |||||
@override | |||||
Stream<SearchState> mapEventToState( | |||||
SearchEvent event, | |||||
) async* { | |||||
/// 初始化方法 | |||||
if (event is SearchInitEvent) { | |||||
yield* _mapInitEventToState(event); | |||||
} | |||||
/// 搜索的方法 | |||||
if (event is SearchSubmitEvent) { | |||||
yield* _mapSearchSubmitToState(event); | |||||
} | |||||
} | |||||
/// 初始化 | |||||
Stream<SearchState> _mapInitEventToState(SearchInitEvent event) async* { | |||||
/// 获取数据 | |||||
var result = await repository.fetchInit(event.model); | |||||
Logger.log('result = ${result.toString()}'); | |||||
if (!EmptyUtil.isEmpty(result)) { | |||||
yield SearchLoadedState(model: result); | |||||
} else { | |||||
yield SearchErrorState(); | |||||
} | |||||
} | |||||
/// 搜索的方法 | |||||
Stream<SearchState> _mapSearchSubmitToState(SearchSubmitEvent event) async* { | |||||
var result = await repository.fetchSearchSubmit(event.model); | |||||
} | |||||
} |
@@ -0,0 +1,28 @@ | |||||
part of 'search_bloc.dart'; | |||||
abstract class SearchEvent extends Equatable { | |||||
const SearchEvent(); | |||||
@override | |||||
List<Object> get props => []; | |||||
} | |||||
/// 初始化方法 | |||||
class SearchInitEvent extends SearchEvent { | |||||
final Map<String, dynamic> model; | |||||
const SearchInitEvent({this.model}); | |||||
@override | |||||
List<Object> get props => [model]; | |||||
} | |||||
/// 搜索方法 | |||||
class SearchSubmitEvent extends SearchEvent { | |||||
final String model; | |||||
const SearchSubmitEvent({@required this.model}); | |||||
@override | |||||
List<Object> get props => [this.model]; | |||||
} |
@@ -0,0 +1,27 @@ | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
class SearchRepository { | |||||
List<Map<String, dynamic>> _pageData = []; | |||||
/// 获取网络数据 | |||||
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); | |||||
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) {} | |||||
/// 搜索的方法 | |||||
Future<void> fetchSearchSubmit(final String model) async { | |||||
var result = await NetUtil.post(''); | |||||
} | |||||
} |
@@ -0,0 +1,36 @@ | |||||
part of 'search_bloc.dart'; | |||||
abstract class SearchState extends Equatable { | |||||
const SearchState(); | |||||
@override | |||||
List<Object> get props => []; | |||||
} | |||||
class SearchInitial extends SearchState { | |||||
@override | |||||
List<Object> get props => []; | |||||
} | |||||
/// 数据加载完毕 | |||||
class SearchLoadedState extends SearchInitial { | |||||
List<Map<String, dynamic>> model; | |||||
SearchLoadedState({this.model}); | |||||
@override | |||||
List<Object> get props => [this.model]; | |||||
} | |||||
/// 搜索成功 | |||||
class SearchSubmitSuccessState extends SearchState{ | |||||
} | |||||
/// 搜索失败 | |||||
class SearchSubmitErrorState extends SearchState{ | |||||
} | |||||
/// 数据加载出错 | |||||
class SearchErrorState extends SearchState {} |
@@ -0,0 +1,137 @@ | |||||
import 'package:flutter/material.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
import 'package:flutter_bloc/flutter_bloc.dart'; | |||||
import 'bloc/search_bloc.dart'; | |||||
import 'bloc/search_repository.dart'; | |||||
/// | |||||
/// 搜索页 | |||||
/// | |||||
class SearchPage extends StatelessWidget { | |||||
final Map<String, dynamic> data; | |||||
SearchPage(this.data, {Key key}) : super(key: key); | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return BlocProvider<SearchBloc>( | |||||
create: (_) => SearchBloc(repository: SearchRepository())..add(SearchInitEvent(model: data)), | |||||
child: SearchPageContianer(), | |||||
); | |||||
} | |||||
} | |||||
class SearchPageContianer extends StatefulWidget { | |||||
@override | |||||
_SearchPageContianerState createState() => _SearchPageContianerState(); | |||||
} | |||||
class _SearchPageContianerState extends State<SearchPageContianer> { | |||||
/// tab轮播 | |||||
TabController _tabController; | |||||
ScrollController _controller = ScrollController(); | |||||
@override | |||||
void initState() { | |||||
_tabController = TabController(length:0, vsync: ScrollableState()); | |||||
super.initState(); | |||||
} | |||||
@override | |||||
void dispose() { | |||||
_controller?.dispose(); | |||||
_tabController?.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: CustomScrollView( | |||||
controller: _controller, | |||||
slivers: _createContent(context, datas ?? []), | |||||
), | |||||
); | |||||
} | |||||
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], | |||||
)); | |||||
} | |||||
list.add(SliverFillRemaining( | |||||
child: Text('etstssss et'), | |||||
)); | |||||
return list; | |||||
} | |||||
} |
@@ -0,0 +1,183 @@ | |||||
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; | |||||
} |
@@ -0,0 +1,148 @@ | |||||
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; | |||||
} | |||||
} | |||||
@@ -1,11 +0,0 @@ | |||||
import 'package:flutter/material.dart'; | |||||
/// | |||||
/// 搜索页 | |||||
/// | |||||
class SreachPage extends StatelessWidget { | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Container(); | |||||
} | |||||
} |
@@ -1,3 +1,4 @@ | |||||
import 'package:flutter/cupertino.dart'; | |||||
import 'package:zhiying_base_widget/pages/goods_details_page/goods_details_page.dart'; | import 'package:zhiying_base_widget/pages/goods_details_page/goods_details_page.dart'; | ||||
import 'package:zhiying_base_widget/pages/home_page/home_page.dart'; | import 'package:zhiying_base_widget/pages/home_page/home_page.dart'; | ||||
import 'package:zhiying_base_widget/pages/main_page/main_page.dart'; | import 'package:zhiying_base_widget/pages/main_page/main_page.dart'; | ||||
@@ -8,7 +9,6 @@ import 'package:zhiying_base_widget/pages/security_page/security_mobile/security | |||||
import 'package:zhiying_base_widget/pages/security_page/security_page.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/security_page/security_password/security_password.dart'; | ||||
import 'package:zhiying_base_widget/pages/setting_page/setting_page.dart'; | import 'package:zhiying_base_widget/pages/setting_page/setting_page.dart'; | ||||
import 'package:zhiying_base_widget/pages/sreach_page/sreach_page.dart'; | |||||
import 'package:zhiying_base_widget/pages/sreach_result_page/sreach_result_page.dart'; | import 'package:zhiying_base_widget/pages/sreach_result_page/sreach_result_page.dart'; | ||||
import 'package:zhiying_base_widget/pages/wallet_page/wallet_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/pages/webview/base_webview.dart'; | ||||
@@ -36,11 +36,16 @@ import 'package:zhiying_base_widget/widgets/wallet/wallet_income/wallet_income.d | |||||
import 'package:zhiying_comm/util/defalut_widget_creater.dart'; | import 'package:zhiying_comm/util/defalut_widget_creater.dart'; | ||||
import 'package:zhiying_comm/zhiying_comm.dart'; | import 'package:zhiying_comm/zhiying_comm.dart'; | ||||
import 'pages/search_page/search_page.dart'; | |||||
import 'widgets/goods_details/coupon/counpon_widget.dart'; | import 'widgets/goods_details/coupon/counpon_widget.dart'; | ||||
import 'widgets/goods_details/detail_img/goods_details_img.dart'; | import 'widgets/goods_details/detail_img/goods_details_img.dart'; | ||||
import 'widgets/goods_details/evaluate/goods_details_evaluate_widget.dart'; | 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/goods_details/title/goods_details_title_widget.dart'; | ||||
import 'widgets/home/home_quick_entry/home_quick_entry.dart'; | import 'widgets/home/home_quick_entry/home_quick_entry.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'; | |||||
class BaseWidgetRegister { | class BaseWidgetRegister { | ||||
/// 初始化方法 | /// 初始化方法 | ||||
@@ -56,7 +61,7 @@ class BaseWidgetRegister { | |||||
PageFactory.regist('profile', (model) => MainPage(model)); | PageFactory.regist('profile', (model) => MainPage(model)); | ||||
PageFactory.regist('category', (model) => WalletPage()); | PageFactory.regist('category', (model) => WalletPage()); | ||||
PageFactory.regist('goods_details', (model) => GoodsDetailsPage(model)); | PageFactory.regist('goods_details', (model) => GoodsDetailsPage(model)); | ||||
PageFactory.regist('sreach', (model) => SreachPage()); | |||||
PageFactory.regist('sreach', (model) => SearchPage(model)); | |||||
PageFactory.regist('sreach_result', (model) => SreachResultPage()); | PageFactory.regist('sreach_result', (model) => SreachResultPage()); | ||||
// PageFactory.regist('login', (model) => LoginPage(model)); | // PageFactory.regist('login', (model) => LoginPage(model)); | ||||
// PageFactory.regist('login_quick', (model) => LoginQuickPage(model)); | // PageFactory.regist('login_quick', (model) => LoginQuickPage(model)); | ||||
@@ -92,86 +97,79 @@ class BaseWidgetRegister { | |||||
// ==================== 首页 | // ==================== 首页 | ||||
// WidgetFactory.regist('index_title', NormalNavCreater()); | // WidgetFactory.regist('index_title', NormalNavCreater()); | ||||
/// 首页搜索栏 | /// 首页搜索栏 | ||||
// WidgetFactory.regist('index_search', HomeSreachCreater()); | |||||
WidgetFactory.regist('index_search', | |||||
DefaultWidgetCreater((model) => HomeSreachWidget(model))); | |||||
WidgetFactory.regist('index_search', HomeSreachCreater()); | |||||
// WidgetFactory.regist('index_search', | |||||
// DefaultWidgetCreater((model) => HomeSreachWidget(model))); | |||||
/// 可滚动banner | /// 可滚动banner | ||||
WidgetFactory.regist('index_carousel', HomeSlideBannerCreater()); | WidgetFactory.regist('index_carousel', HomeSlideBannerCreater()); | ||||
WidgetFactory.regist('index_recommend_list', GoodsListCreater()); | 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 | /// 不可以滚动banner | ||||
WidgetFactory.regist('index_banner_one', HomeBannerCreater()); | WidgetFactory.regist('index_banner_one', HomeBannerCreater()); | ||||
WidgetFactory.regist('index_banner_two', HomeBannerCreater()); | WidgetFactory.regist('index_banner_two', HomeBannerCreater()); | ||||
// WidgetFactory.regist('index_taobao_auth_tip', HomeAuthCreater()); | // WidgetFactory.regist('index_taobao_auth_tip', HomeAuthCreater()); | ||||
/// ==================== 搜索页面 ==================== /// | |||||
// 搜索标题 | |||||
// 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))); | |||||
// // 搜索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_history', null); | |||||
/// ==================== 商品详情 ==================== /// | /// ==================== 商品详情 ==================== /// | ||||
// 商品详情轮播图 | // 商品详情轮播图 | ||||
WidgetFactory.regist('product_detail_carousel', | |||||
DefaultWidgetCreater((model) => GoodsDetailsSlideBannerWidget(model))); | |||||
WidgetFactory.regist('product_detail_carousel', DefaultWidgetCreater((model) => GoodsDetailsSlideBannerWidget(model))); | |||||
// 商品详情下载APP提示 | // 商品详情下载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', | |||||
DefaultWidgetCreater((model) => GoodsListWidget(model))); | |||||
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_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( | |||||
'wallet_data', DefaultWidgetCreater((model) => WalletData())); | |||||
WidgetFactory.regist('wallet_data', DefaultWidgetCreater((model) => WalletData())); | |||||
// WidgetFactory.regist( | // WidgetFactory.regist( | ||||
// 'wallet_detail', DefaultWidgetCreater((model) => WalletDetail())); | // 'wallet_detail', DefaultWidgetCreater((model) => WalletDetail())); | ||||
// WidgetFactory.regist('wallet_detail', HomeAuthCreater()); | // WidgetFactory.regist('wallet_detail', HomeAuthCreater()); | ||||
WidgetFactory.regist( | |||||
'wallet_data', DefaultWidgetCreater((model) => WalletData())); | |||||
WidgetFactory.regist( | |||||
'wallet_detail', DefaultWidgetCreater((model) => WalletDetail())); | |||||
WidgetFactory.regist('wallet_data', DefaultWidgetCreater((model) => WalletData())); | |||||
WidgetFactory.regist('wallet_detail', DefaultWidgetCreater((model) => WalletDetail())); | |||||
WidgetFactory.regist( | |||||
'wallet_income', DefaultWidgetCreater((model) => WalletIncome())); | |||||
WidgetFactory.regist('wallet_income', DefaultWidgetCreater((model) => WalletIncome())); | |||||
} | } | ||||
} | } |
@@ -61,8 +61,8 @@ class _HomeBannerContainerState extends State<HomeBannerContainer> { | |||||
builder: (context, state) { | builder: (context, state) { | ||||
print(state); | print(state); | ||||
if (state is HomeBannerLoadedState) { | if (state is HomeBannerLoadedState) { | ||||
int lenght = state?.model?.list?.length ?? 0; | |||||
if (lenght > 0) | |||||
int length = state?.model?.list?.length ?? 0; | |||||
if (length > 0) | |||||
return _getMainWidget(data: state?.model?.list); | return _getMainWidget(data: state?.model?.list); | ||||
else | else | ||||
return HomeBannerSkeleton(widget?.model); | return HomeBannerSkeleton(widget?.model); | ||||
@@ -0,0 +1,26 @@ | |||||
class SearchAppbarModel { | |||||
String app_bar_bg_color; | |||||
String app_bar_title; | |||||
String app_bar_title_color; | |||||
String back_icon; | |||||
SearchAppbarModel({this.app_bar_bg_color, this.app_bar_title, this.app_bar_title_color, this.back_icon}); | |||||
factory SearchAppbarModel.fromJson(Map<String, dynamic> json) { | |||||
return SearchAppbarModel( | |||||
app_bar_bg_color: json['app_bar_bg_color'], | |||||
app_bar_title: json['app_bar_title'], | |||||
app_bar_title_color: json['app_bar_title_color'], | |||||
back_icon: json['back_icon'], | |||||
); | |||||
} | |||||
Map<String, dynamic> toJson() { | |||||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
data['app_bar_bg_color'] = this.app_bar_bg_color; | |||||
data['app_bar_title'] = this.app_bar_title; | |||||
data['app_bar_title_color'] = this.app_bar_title_color; | |||||
data['back_icon'] = this.back_icon; | |||||
return data; | |||||
} | |||||
} |
@@ -0,0 +1,28 @@ | |||||
import 'package:flutter/cupertino.dart'; | |||||
import 'package:flutter/src/widgets/framework.dart'; | |||||
import 'package:zhiying_base_widget/widgets/search/appbar/search_appbar_widget.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
import 'dart:ui'; | |||||
import 'package:zhiying_comm/util/custom_sliver_persistent_header_delegate.dart'; | |||||
class SearchAppbarCreater extends WidgetCreater { | |||||
@override | |||||
List<Widget> createWidgets(Map<String, dynamic> model) { | |||||
return [ | |||||
SliverPersistentHeader( | |||||
delegate: CustomSliverPersistentHeaderDelegate( | |||||
child: SearchAppbarWidget(model), | |||||
max: MediaQueryData.fromWindow(window).padding.top + 44, | |||||
min: MediaQueryData.fromWindow(window).padding.top + 44, | |||||
), | |||||
pinned: true, | |||||
floating: false, | |||||
), | |||||
]; | |||||
} | |||||
@override | |||||
bool isSliverChild() { | |||||
return true; | |||||
} | |||||
} |
@@ -0,0 +1,55 @@ | |||||
import 'dart:convert'; | |||||
import 'package:flutter/material.dart'; | |||||
import 'package:zhiying_base_widget/widgets/search/appbar/model/search_appbar_model.dart'; | |||||
import 'dart:ui'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
class SearchAppbarWidget extends StatelessWidget { | |||||
final Map<String, dynamic> data; | |||||
SearchAppbarModel model; | |||||
SearchAppbarWidget(this.data, {Key key}) : super(key: key) { | |||||
try { | |||||
model = SearchAppbarModel.fromJson(jsonDecode(data['data'])); | |||||
} catch (e) { | |||||
Logger.error(e); | |||||
} | |||||
} | |||||
/// 返回键 | |||||
void _openPop(BuildContext context) { | |||||
Navigator.maybePop(context); | |||||
} | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Container( | |||||
width: double.infinity, | |||||
color: HexColor.fromHex(model?.app_bar_bg_color ?? '#FFFFFF'), | |||||
padding: EdgeInsets.only(top: MediaQueryData.fromWindow(window).padding.top), | |||||
child: _getMainWidget(context), | |||||
); | |||||
} | |||||
/// 样式1 | |||||
Widget _getMainWidget(BuildContext context) { | |||||
return AppBar( | |||||
backgroundColor: Colors.transparent, | |||||
elevation: 0, | |||||
leading: IconButton( | |||||
icon: Icon( | |||||
Icons.arrow_back_ios, | |||||
size: 22, | |||||
color: HexColor.fromHex('#333333'), | |||||
), | |||||
onPressed: () => _openPop(context), | |||||
), | |||||
title: Text( | |||||
model?.app_bar_title ?? '搜索', | |||||
style: TextStyle(fontWeight: FontWeight.bold, color: HexColor.fromHex(model?.app_bar_title_color ?? '#333333')), | |||||
), | |||||
centerTitle: true, | |||||
); | |||||
} | |||||
} |
@@ -0,0 +1,129 @@ | |||||
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,32 @@ | |||||
class SearchInputModel { | |||||
String search_button; | |||||
String search_button_color; | |||||
String search_button_t; | |||||
String search_icon; | |||||
String search_inpu_hint_text; | |||||
String skip_identifier; | |||||
SearchInputModel({this.search_button, this.search_button_color, this.search_button_t, this.search_icon, this.search_inpu_hint_text, this.skip_identifier}); | |||||
factory SearchInputModel.fromJson(Map<String, dynamic> json) { | |||||
return SearchInputModel( | |||||
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'], | |||||
skip_identifier: json['skip_identifier'], | |||||
); | |||||
} | |||||
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['skip_identifier'] = this.skip_identifier; | |||||
return data; | |||||
} | |||||
} |
@@ -0,0 +1,27 @@ | |||||
import 'package:flutter/material.dart'; | |||||
import 'package:flutter/src/widgets/framework.dart'; | |||||
import 'package:zhiying_base_widget/widgets/search/input/search_input_widget.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
import 'package:zhiying_comm/util/custom_sliver_persistent_header_delegate.dart'; | |||||
class SearchInputCreater extends WidgetCreater { | |||||
@override | |||||
List<Widget> createWidgets(Map<String, dynamic> model) { | |||||
return [ | |||||
SliverPersistentHeader( | |||||
delegate: CustomSliverPersistentHeaderDelegate( | |||||
child: SearchInputWidget(model), | |||||
min: 35, | |||||
max: 35, | |||||
), | |||||
pinned: true, | |||||
floating: false, | |||||
) | |||||
]; | |||||
} | |||||
@override | |||||
bool isSliverChild() { | |||||
return true; | |||||
} | |||||
} |
@@ -0,0 +1,36 @@ | |||||
import 'package:shimmer/shimmer.dart'; | |||||
import 'package:flutter/material.dart'; | |||||
/// | |||||
/// 搜索输入框的骨架图 | |||||
/// | |||||
class SearchInputSkeleton extends StatelessWidget { | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Container( | |||||
width: double.infinity, | |||||
margin: const EdgeInsets.only(right: 12.5, left: 12.5, top: 13), | |||||
child: _shimmerWidget( | |||||
width: double.infinity, | |||||
height: 32, | |||||
), | |||||
); | |||||
} | |||||
Widget _shimmerWidget({double width, double height, double radius = 0}) { | |||||
return Shimmer.fromColors( | |||||
baseColor: Colors.grey[300], | |||||
highlightColor: Colors.grey[100], | |||||
child: Container( | |||||
width: width, | |||||
height: height, | |||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)), | |||||
), | |||||
); | |||||
} | |||||
} |
@@ -0,0 +1,172 @@ | |||||
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/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:cached_network_image/cached_network_image.dart'; | |||||
/// | |||||
/// 搜索页的搜索框 | |||||
/// | |||||
class SearchInputWidget extends StatefulWidget { | |||||
final Map<String, dynamic> data; | |||||
SearchInputModel model; | |||||
SearchInputWidget(this.data, {Key key}) : super(key: key) { | |||||
try { | |||||
model = SearchInputModel.fromJson(jsonDecode(data['data'])); | |||||
} catch (e) { | |||||
Logger.error(e); | |||||
} | |||||
} | |||||
@override | |||||
_SearchInputWidgetState createState() => _SearchInputWidgetState(); | |||||
} | |||||
class _SearchInputWidgetState extends State<SearchInputWidget> { | |||||
/// 点击搜索按钮 | |||||
void _onSearchButtomClick() {} | |||||
FocusNode _focusNode; | |||||
TextEditingController _editingController; | |||||
@override | |||||
void didChangeDependencies() { | |||||
super.didChangeDependencies(); | |||||
} | |||||
@override | |||||
void initState() { | |||||
_focusNode = FocusNode(); | |||||
_editingController = TextEditingController(); | |||||
super.initState(); | |||||
} | |||||
@override | |||||
void dispose() { | |||||
_focusNode?.unfocus(); | |||||
_focusNode?.dispose(); | |||||
_editingController?.dispose(); | |||||
super.dispose(); | |||||
} | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Visibility( | |||||
visible: !EmptyUtil.isEmpty(widget?.model), | |||||
replacement: SearchInputSkeleton(), | |||||
child: _getMainWidget(widget?.model), | |||||
); | |||||
} | |||||
/// 获取主视图 | |||||
Widget _getMainWidget(SearchInputModel model) { | |||||
return 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), | |||||
], | |||||
), | |||||
); | |||||
} | |||||
/// 搜索icon | |||||
Widget _getSearchIconWidget(SearchInputModel model) { | |||||
return Container( | |||||
height: double.infinity, | |||||
width: 20, | |||||
margin: const EdgeInsets.only(left: 12.5), | |||||
padding: const EdgeInsets.symmetric(vertical: 6), | |||||
child: CachedNetworkImage( | |||||
imageUrl: model?.search_icon ?? '', | |||||
), | |||||
); | |||||
} | |||||
/// 搜索输入框 | |||||
Widget _getSearchInputWidget(SearchInputModel model) { | |||||
return Container( | |||||
height: double.infinity, | |||||
alignment: Alignment.centerLeft, | |||||
// padding: const EdgeInsets.symmetric(vertical: 6), | |||||
child: TextField( | |||||
showCursor: true, | |||||
cursorWidth: 1, | |||||
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: '搜索更多优惠商品', | |||||
hintStyle: TextStyle(color: HexColor.fromHex('#999999'), fontSize: 14), | |||||
), | |||||
), | |||||
); | |||||
} | |||||
/// 搜索按钮 | |||||
Widget _getSearchButtomWidget(SearchInputModel model) { | |||||
return GestureDetector( | |||||
behavior: HitTestBehavior.opaque, | |||||
onTap: () => _onSearchButtomClick(), | |||||
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), | |||||
borderRadius: BorderRadius.circular(30), | |||||
), | |||||
child: Text( | |||||
'搜索', | |||||
style: TextStyle(color: HexColor.fromHex('#FFFFFF'), fontSize: 14), | |||||
), | |||||
), | |||||
); | |||||
} | |||||
/// 【弃用】搜索按钮 | |||||
// Widget _getSearchButtomWidget() { | |||||
// return Material( | |||||
// child: Container( | |||||
// decoration: BoxDecoration( | |||||
// borderRadius: BorderRadius.only(topRight: Radius.circular(25), bottomRight: Radius.circular(25)) | |||||
// ), | |||||
// height: double.infinity, | |||||
// width: 63, | |||||
// child: RaisedButton( | |||||
// padding: const EdgeInsets.only(bottom: 6, top: 6, left: 17.5, right: 17.5), | |||||
// shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( 25)), | |||||
// child: Text('搜索', style: TextStyle( fontSize: 14, color: HexColor.fromHex('#FFFFFF')),), | |||||
// onPressed: ()=> _onSearchButtomClick(), | |||||
// color: HexColor.fromHex('#FF0100'), | |||||
// ), | |||||
// ), | |||||
// ); | |||||
// } | |||||
} |
@@ -0,0 +1,55 @@ | |||||
class SearchTabModel { | |||||
List<SearchTabItemModel> search_icon_list; | |||||
SearchTabModel({this.search_icon_list}); | |||||
factory SearchTabModel.fromJson(Map<String, dynamic> json) { | |||||
return SearchTabModel( | |||||
search_icon_list: json['search_icon_list'] != null ? (json['search_icon_list'] as List).map((i) => SearchTabItemModel.fromJson(i)).toList() : null, | |||||
); | |||||
} | |||||
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(); | |||||
} | |||||
return data; | |||||
} | |||||
} | |||||
class SearchTabItemModel { | |||||
String icon; | |||||
String line_select_color; | |||||
String name; | |||||
String name_color; | |||||
String name_select_color; | |||||
String type; | |||||
String 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}); | |||||
factory SearchTabItemModel.fromJson(Map<String, dynamic> json) { | |||||
return SearchTabItemModel( | |||||
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'], | |||||
); | |||||
} | |||||
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; | |||||
return data; | |||||
} | |||||
} |
@@ -0,0 +1,24 @@ | |||||
import 'package:flutter/cupertino.dart'; | |||||
import 'package:zhiying_base_widget/widgets/search/tabbar/search_tab_widget.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
import 'package:zhiying_comm/util/sliver_tab_bar_delegate.dart'; | |||||
class SearcchTabCreater extends WidgetCreater{ | |||||
@override | |||||
List<Widget> createWidgets(Map<String, dynamic> model) { | |||||
return [ | |||||
// SliverPersistentHeader( | |||||
// delegate: SliverTabBarDelegate( | |||||
// SearchTabWidget(model), | |||||
// ), | |||||
// ), | |||||
SearchTabWidget(model), | |||||
]; | |||||
} | |||||
@override | |||||
bool isSliverChild() { | |||||
return true; | |||||
} | |||||
} |
@@ -0,0 +1,22 @@ | |||||
import 'package:flutter/material.dart'; | |||||
import 'package:zhiying_comm/util/shimmer_util.dart'; | |||||
class SearchTabSkeleton 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,105 @@ | |||||
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/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_comm/zhiying_comm.dart'; | |||||
import 'model/search_tab_model.dart'; | |||||
class SearchTabWidget extends StatefulWidget { | |||||
final Map<String, dynamic> data; | |||||
SearchTabModel model; | |||||
SearchTabWidget(this.data, {Key key}) : super(key: key) { | |||||
try { | |||||
model = SearchTabModel.fromJson(jsonDecode(data['data'])); | |||||
} catch (e) { | |||||
Logger.error(e.toString()); | |||||
} | |||||
} | |||||
@override | |||||
_SearchTabWidgetState createState() => _SearchTabWidgetState(); | |||||
} | |||||
class _SearchTabWidgetState extends State<SearchTabWidget> { | |||||
TabController _tabController; | |||||
int _currentIndex = 0; | |||||
@override | |||||
void initState() { | |||||
_tabController = TabController(length: widget?.model?.search_icon_list?.length ?? 0, vsync: ScrollableState())..addListener((){ | |||||
setState(()=> _currentIndex = _tabController.index); | |||||
}); | |||||
super.initState(); | |||||
} | |||||
@override | |||||
void dispose() { | |||||
_tabController?.dispose(); | |||||
super.dispose(); | |||||
} | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return _getMainWidget(widget?.model); | |||||
} | |||||
/// 获取主视图 | |||||
Widget _getMainWidget(SearchTabModel model) { | |||||
return Visibility( | |||||
replacement: SearchTabSkeleton(), | |||||
visible: !EmptyUtil.isEmpty(model), | |||||
child: _getTabar(model), | |||||
); | |||||
} | |||||
/// 获取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(), | |||||
), | |||||
); | |||||
} | |||||
} | |||||
class SearchTabItemWidget extends StatelessWidget { | |||||
final bool isSelect; | |||||
final SearchTabItemModel model; | |||||
const SearchTabItemWidget(this.isSelect, this.model); | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Container(); | |||||
} | |||||
} | |||||
@@ -0,0 +1,19 @@ | |||||
import 'dart:math'; | |||||
import 'package:flutter/cupertino.dart'; | |||||
import 'package:flutter/material.dart'; | |||||
/** | |||||
* 创建人: Created by zhaolong | |||||
* 创建时间:Created by on 2020/6/26. | |||||
* | |||||
* 可关注公众号:我的大前端生涯 获取最新技术分享 | |||||
* 可关注网易云课堂:https://study.163.com/instructor/1021406098.htm | |||||
* 可关注博客:https://blog.csdn.net/zl18603543572 | |||||
*/ | |||||
Color getRandomColor() { | |||||
return Color.fromARGB(255, Random.secure().nextInt(200), | |||||
Random.secure().nextInt(200), Random.secure().nextInt(200)); | |||||
} |
@@ -0,0 +1,83 @@ | |||||
import 'package:flutter/material.dart'; | |||||
import 'package:flutter/rendering.dart'; | |||||
const double _kTabHeight = 46.0; | |||||
const double _kTextAndIconTabHeight = 40.0; | |||||
/// A material design [TabBar] tab. | |||||
/// | |||||
/// If both [icon] and [text] are provided, the text is displayed below | |||||
/// the icon. | |||||
/// | |||||
/// See also: | |||||
/// | |||||
/// * [TabBar], which displays a row of tabs. | |||||
/// * [TabBarView], which displays a widget for the currently selected tab. | |||||
/// * [TabController], which coordinates tab selection between a [TabBar] and a [TabBarView]. | |||||
/// * <https://material.io/design/components/tabs.html> | |||||
class MyTab extends StatelessWidget { | |||||
/// Creates a material design [TabBar] tab. | |||||
/// | |||||
/// At least one of [text], [icon], and [child] must be non-null. The [text] | |||||
/// and [child] arguments must not be used at the same time. | |||||
const MyTab({ | |||||
Key key, | |||||
this.text, | |||||
this.icon, | |||||
this.child, | |||||
}) : assert(text != null || child != null || icon != null), | |||||
assert(!(text != null && null != child)), | |||||
// TODO(goderbauer): https://github.com/dart-lang/sdk/issues/34180 | |||||
super(key: key); | |||||
/// The text to display as the tab's label. | |||||
/// | |||||
/// Must not be used in combination with [child]. | |||||
final String text; | |||||
/// The widget to be used as the tab's label. | |||||
/// | |||||
/// Usually a [Text] widget, possibly wrapped in a [Semantics] widget. | |||||
/// | |||||
/// Must not be used in combination with [text]. | |||||
final Widget child; | |||||
/// An icon to display as the tab's label. | |||||
final Widget icon; | |||||
Widget _buildLabelText() { | |||||
return child ?? Text(text, softWrap: false, overflow: TextOverflow.fade); | |||||
} | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
assert(debugCheckHasMaterial(context)); | |||||
double height; | |||||
Widget label; | |||||
if (icon == null) { | |||||
height = _kTabHeight; | |||||
label = _buildLabelText(); | |||||
} else if (text == null && child == null) { | |||||
height = _kTabHeight; | |||||
label = icon; | |||||
} else { | |||||
height = _kTextAndIconTabHeight; | |||||
label = Row( | |||||
children: <Widget>[ | |||||
Container(child: icon, margin: const EdgeInsets.only(right: 5),), | |||||
_buildLabelText(), | |||||
], | |||||
); | |||||
} | |||||
return SizedBox( | |||||
height: height, | |||||
child: Center( | |||||
child: label, | |||||
widthFactor: 1.0, | |||||
), | |||||
); | |||||
} | |||||
} |
@@ -0,0 +1,81 @@ | |||||
import 'package:flutter/cupertino.dart'; | |||||
import 'package:flutter/material.dart'; | |||||
import 'color_utils.dart'; | |||||
/** | |||||
* 创建人: Created by zhaolong | |||||
* 创建时间:Created by on 2020/6/26. | |||||
* | |||||
* 可关注公众号:我的大前端生涯 获取最新技术分享 | |||||
* 可关注网易云课堂:https://study.163.com/instructor/1021406098.htm | |||||
* 可关注博客:https://blog.csdn.net/zl18603543572 | |||||
*/ | |||||
class TextTagWidget extends StatefulWidget { | |||||
///显示的文本 | |||||
String text; | |||||
EdgeInsets margin; | |||||
EdgeInsets padding; | |||||
TextStyle textStyle; | |||||
Color backgroundColor; | |||||
Color borderColor; | |||||
double borderRadius; | |||||
TextTagWidget( | |||||
this.text, { | |||||
this.margin = const EdgeInsets.all(4), | |||||
this.padding = const EdgeInsets.only(left: 6, right: 6, top: 3, bottom: 3), | |||||
this.textStyle, | |||||
this.backgroundColor, | |||||
this.borderColor, | |||||
this.borderRadius = 20.0, | |||||
}) { | |||||
if (this.borderColor == null) { | |||||
if (this.backgroundColor != null) { | |||||
this.borderColor = this.backgroundColor; | |||||
} else { | |||||
this.borderColor = getRandomColor(); | |||||
} | |||||
} | |||||
if (this.textStyle == null) { | |||||
Color textColor = this.borderColor; | |||||
if (backgroundColor != null) { | |||||
textColor = Colors.white; | |||||
} | |||||
this.textStyle = TextStyle(fontSize: 12, color: textColor); | |||||
} | |||||
if (this.backgroundColor == null) { | |||||
this.backgroundColor = Colors.transparent; | |||||
} | |||||
} | |||||
@override | |||||
_TestPageState createState() => _TestPageState(); | |||||
} | |||||
//lib/code/main_data.dart | |||||
class _TestPageState extends State<TextTagWidget> { | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
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)), | |||||
child: buildTextWidget(), | |||||
); | |||||
} | |||||
///构建文本 | |||||
Text buildTextWidget() { | |||||
return Text( | |||||
widget.text, | |||||
style: widget.textStyle, | |||||
textAlign: TextAlign.center, | |||||
); | |||||
} | |||||
} |
@@ -0,0 +1,56 @@ | |||||
import 'package:flutter/material.dart'; | |||||
import 'package:cached_network_image/cached_network_image.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
class SearchTitleWidget extends StatelessWidget { | |||||
final String titleText; | |||||
final String titleTextColor; | |||||
final String iconUrl; | |||||
final VoidCallback callback; | |||||
const SearchTitleWidget({ | |||||
this.titleText, | |||||
this.titleTextColor, | |||||
this.iconUrl, | |||||
this.callback, | |||||
}); | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Container( | |||||
width: double.infinity, | |||||
child: Row( | |||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, | |||||
children: <Widget>[ | |||||
/// 标题 | |||||
_title(), | |||||
/// icon | |||||
Visibility(visible: !EmptyUtil.isEmpty(iconUrl), child: _icon()), | |||||
], | |||||
), | |||||
); | |||||
} | |||||
Widget _title() { | |||||
return Text( | |||||
titleText, | |||||
style: TextStyle( | |||||
fontSize: 15, | |||||
fontWeight: FontWeight.bold, | |||||
color: HexColor.fromHex(titleTextColor), | |||||
), | |||||
); | |||||
} | |||||
Widget _icon() { | |||||
return GestureDetector( | |||||
onTap: callback, | |||||
child: CachedNetworkImage( | |||||
imageUrl: iconUrl ?? '', | |||||
width: 15, | |||||
fit: BoxFit.fill, | |||||
), | |||||
); | |||||
} | |||||
} |
@@ -17,6 +17,7 @@ dependencies: | |||||
pull_to_refresh: ^1.6.1 | pull_to_refresh: ^1.6.1 | ||||
flutter_cupertino_date_picker: ^1.0.26+2 | flutter_cupertino_date_picker: ^1.0.26+2 | ||||
image_picker: ^0.6.7+3 | image_picker: ^0.6.7+3 | ||||
tab_indicator_styler: 1.0.0 | |||||
image_cropper: | image_cropper: | ||||
git: | git: | ||||
url: 'http://192.168.0.138:3000/FnuoOS_Flutter_Components/Image_Cropper.git' | url: 'http://192.168.0.138:3000/FnuoOS_Flutter_Components/Image_Cropper.git' | ||||