@@ -55,8 +55,7 @@ class GoodsDetailsContainer extends StatefulWidget { | |||
class _GoodsDetailsContainerState extends State<GoodsDetailsContainer> { | |||
bool _isEnded = false; | |||
ScrollController _controller = ScrollController(); | |||
RefreshController _refreshController = | |||
RefreshController(initialRefresh: false); | |||
RefreshController _refreshController = RefreshController(initialRefresh: false); | |||
void _onLoading() async { | |||
// 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/home_page/home_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_password/security_password.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/wallet_page/wallet_page.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/zhiying_comm.dart'; | |||
import 'pages/search_page/search_page.dart'; | |||
import 'widgets/goods_details/coupon/counpon_widget.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/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/search/input/search_input_widget.dart'; | |||
import 'widgets/search/tabbar/search_tab_creater.dart'; | |||
import 'widgets/search/tabbar/search_tab_widget.dart'; | |||
class BaseWidgetRegister { | |||
/// 初始化方法 | |||
@@ -56,7 +61,7 @@ class BaseWidgetRegister { | |||
PageFactory.regist('profile', (model) => MainPage(model)); | |||
PageFactory.regist('category', (model) => WalletPage()); | |||
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('login', (model) => LoginPage(model)); | |||
// PageFactory.regist('login_quick', (model) => LoginQuickPage(model)); | |||
@@ -92,86 +97,79 @@ class BaseWidgetRegister { | |||
// ==================== 首页 | |||
// 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 | |||
WidgetFactory.regist('index_carousel', HomeSlideBannerCreater()); | |||
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()); | |||
WidgetFactory.regist('index_banner_two', HomeBannerCreater()); | |||
// 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提示 | |||
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_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( | |||
// 'wallet_detail', DefaultWidgetCreater((model) => WalletDetail())); | |||
// 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) { | |||
print(state); | |||
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); | |||
else | |||
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 | |||
flutter_cupertino_date_picker: ^1.0.26+2 | |||
image_picker: ^0.6.7+3 | |||
tab_indicator_styler: 1.0.0 | |||
image_cropper: | |||
git: | |||
url: 'http://192.168.0.138:3000/FnuoOS_Flutter_Components/Image_Cropper.git' | |||