| @@ -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/launch_page/launch_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 { | |||
| /// 初始化方法 | |||
| @@ -63,7 +68,7 @@ class BaseWidgetRegister { | |||
| PageFactory.regist('pub.flutter.profile', (model) => MainPage(model)); | |||
| PageFactory.regist('pub.flutter.my_wallet', (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)); | |||
| @@ -99,86 +104,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' | |||