2、商品详情的Notifier修改 3、首页去掉 暂无数据 提示 4、微信导师的协议修改 5、商品列表的骨架图添加tags/0.0.2+11
@@ -0,0 +1,67 @@ | |||
import 'dart:async'; | |||
import 'package:bloc/bloc.dart'; | |||
import 'package:zhiying_base_widget/pages/custom_page/bloc/custom_item_page_repository.dart'; | |||
import 'custom_item_page_state.dart'; | |||
import 'custom_item_page_event.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class CustomItemPageBloc extends Bloc<CustomItemPageEvent, CustomItemPageState> { | |||
// CustomItemPageBloc() : super(CustomItemPageInitial()); | |||
CustomItemPageRepository repository; | |||
CustomItemPageBloc(this.repository); | |||
@override | |||
CustomItemPageState get initialState => CustomItemPageInitial(); | |||
@override | |||
Stream<CustomItemPageState> mapEventToState( | |||
CustomItemPageEvent event, | |||
) async* { | |||
/// 初始化 | |||
if (event is CustomItemPageInitEvent) { | |||
yield* _mapInitEventToState(event); | |||
} | |||
/// 下拉刷新 | |||
if (event is CustomItemPageRefreshEvent) { | |||
yield* _mapRefreshEventToState(event); | |||
} | |||
/// 上拉更多 | |||
if (event is CustomItemPageLoadEvent) { | |||
yield* _mapLoadEventToState(event); | |||
} | |||
} | |||
/// 初始化 | |||
Stream<CustomItemPageState> _mapInitEventToState(CustomItemPageInitEvent event) async* { | |||
var cacheStyle = await repository.fetchCacheStyle(); | |||
if (!EmptyUtil.isEmpty(cacheStyle)) { | |||
yield CustomItemPageLoadedState(model: cacheStyle); | |||
} | |||
var netStyle = await repository.fetchNetStyle(); | |||
if (!EmptyUtil.isEmpty(netStyle) && netStyle is List) { | |||
yield CustomItemPageLoadedState(model: netStyle); | |||
} else { | |||
yield CustomItemPageInitErrorState(); | |||
} | |||
} | |||
/// 下拉刷新 | |||
Stream<CustomItemPageState> _mapRefreshEventToState(CustomItemPageRefreshEvent event) async* { | |||
var netStyle = await repository.fetchNetStyle(); | |||
if (!EmptyUtil.isEmpty(netStyle)) { | |||
yield CustomItemPageRefreshSuccessState(); | |||
yield CustomItemPageLoadedState(model: netStyle); | |||
} else { | |||
yield CustomItemPageRefreshErrorState(); | |||
yield CustomItemPageErrorState(); | |||
} | |||
} | |||
/// 上拉更多 | |||
Stream<CustomItemPageState> _mapLoadEventToState(CustomItemPageLoadEvent event) async* {} | |||
} |
@@ -0,0 +1,13 @@ | |||
import 'package:meta/meta.dart'; | |||
@immutable | |||
abstract class CustomItemPageEvent {} | |||
/// 初始化 | |||
class CustomItemPageInitEvent extends CustomItemPageEvent {} | |||
/// 下拉刷新 | |||
class CustomItemPageRefreshEvent extends CustomItemPageEvent {} | |||
/// 上拉更多 | |||
class CustomItemPageLoadEvent extends CustomItemPageEvent {} |
@@ -0,0 +1,56 @@ | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class CustomItemPageRepository { | |||
final Map<String, dynamic> data; | |||
final int tabIndex; | |||
final String modId; | |||
final String modPid; | |||
CustomItemPageRepository(this.data, this.tabIndex, this.modId, this.modPid); | |||
/// 获取网络Style | |||
Future<List<Map<String, dynamic>>> fetchNetStyle() async { | |||
try { | |||
String reqUrl = _buildRequestUrl(); | |||
if (!EmptyUtil.isEmpty(reqUrl)) { | |||
var result = await NetUtil.post(reqUrl, method: NetMethod.GET, cache: true); | |||
if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { | |||
List mobList = !EmptyUtil.isEmpty(data) ? List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA][tabIndex.toString()]) : List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||
if (!EmptyUtil.isEmpty(mobList)) { | |||
return mobList.map((e) => Map<String, dynamic>.from(e)).toList(); | |||
} | |||
} | |||
} | |||
} catch (e, s) { | |||
Logger.error(e, s); | |||
} | |||
return null; | |||
} | |||
/// 获取缓存Style | |||
Future<List<Map<String, dynamic>>> fetchCacheStyle() async { | |||
try { | |||
String reqUrl = _buildRequestUrl(); | |||
if (!EmptyUtil.isEmpty(reqUrl)) { | |||
var result = await NetUtil.getRequestCachedData(reqUrl); | |||
if (!EmptyUtil.isEmpty(result)) { | |||
List mobList = !EmptyUtil.isEmpty(data) ? List.from(result[tabIndex.toString()]) : List.from(result); | |||
if (!EmptyUtil.isEmpty(mobList)) { | |||
return mobList.map((e) => Map<String, dynamic>.from(e)).toList(); | |||
} | |||
} | |||
} | |||
} catch (e, s) { | |||
Logger.error(e, s); | |||
} | |||
return null; | |||
} | |||
String _buildRequestUrl() { | |||
if (!EmptyUtil.isEmpty(data)) { | |||
return '/api/v1/custom/mod/$modId'; | |||
} else { | |||
return '/api/v1/custom/mod/${modPid}?is_cate=1'; | |||
} | |||
} | |||
} |
@@ -0,0 +1,32 @@ | |||
import 'package:meta/meta.dart'; | |||
@immutable | |||
abstract class CustomItemPageState {} | |||
/// 骨架图状态 | |||
class CustomItemPageInitial extends CustomItemPageState {} | |||
/// 数据加载成功 | |||
class CustomItemPageLoadedState extends CustomItemPageState { | |||
final List<Map<String, dynamic>> model; | |||
CustomItemPageLoadedState({this.model}); | |||
} | |||
/// 初始化失败 | |||
class CustomItemPageInitErrorState extends CustomItemPageState {} | |||
/// 下拉刷新成功 | |||
class CustomItemPageRefreshSuccessState extends CustomItemPageState {} | |||
/// 下拉刷新失败 | |||
class CustomItemPageRefreshErrorState extends CustomItemPageState {} | |||
/// 上拉更多成功 | |||
class CustomItemPageLoadSuccessState extends CustomItemPageState {} | |||
/// 上拉更多失败 | |||
class CustomItemPageLoadErrorState extends CustomItemPageState {} | |||
/// 其他错误 | |||
class CustomItemPageErrorState extends CustomItemPageState {} |
@@ -8,12 +8,16 @@ class CustomPageRepository { | |||
/// 初始化 | |||
Future<List<Map<String, dynamic>>> fetchInitData() async { | |||
try { | |||
String skipIdentifier = !EmptyUtil.isEmpty(data) && data.containsKey(GlobalConfig.SKIP_IDENTIFIER) && !EmptyUtil.isEmpty(data[GlobalConfig.SKIP_IDENTIFIER]) | |||
? data[GlobalConfig.SKIP_IDENTIFIER] | |||
: null; | |||
String skipIdentifier = | |||
!EmptyUtil.isEmpty(data) && data.containsKey(GlobalConfig.SKIP_IDENTIFIER) && !EmptyUtil.isEmpty(data[GlobalConfig.SKIP_IDENTIFIER]) ? data[GlobalConfig.SKIP_IDENTIFIER] : null; | |||
if (!EmptyUtil.isEmpty(skipIdentifier)) { | |||
var result = await NetUtil.post('/api/v1/mod/$skipIdentifier', method: NetMethod.GET); | |||
if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) {} | |||
var result = await NetUtil.post('/api/v1/mod/$skipIdentifier', method: NetMethod.GET, cache: true); | |||
if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { | |||
List mobList = List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['mod_list']); | |||
if (!EmptyUtil.isEmpty(mobList)) { | |||
return mobList.map((e) => Map<String, dynamic>.from(e)).toList(); | |||
} | |||
} | |||
} | |||
} catch (e, s) { | |||
Logger.error(e, s); | |||
@@ -23,7 +27,19 @@ class CustomPageRepository { | |||
/// 缓存数据 | |||
Future<List<Map<String, dynamic>>> fetchCacheData() async { | |||
try {} catch (e, s) { | |||
try { | |||
String skipIdentifier = | |||
!EmptyUtil.isEmpty(data) && data.containsKey(GlobalConfig.SKIP_IDENTIFIER) && !EmptyUtil.isEmpty(data[GlobalConfig.SKIP_IDENTIFIER]) ? data[GlobalConfig.SKIP_IDENTIFIER] : null; | |||
if (!EmptyUtil.isEmpty(skipIdentifier)) { | |||
var result = await NetUtil.getRequestCachedData('/api/v1/mod/$skipIdentifier'); | |||
if (!EmptyUtil.isEmpty(result)) { | |||
List mobList = result['mod_list']; | |||
if (!EmptyUtil.isEmpty(mobList)) { | |||
return mobList.map((e) => Map<String, dynamic>.from(e)).toList(); | |||
} | |||
} | |||
} | |||
} catch (e, s) { | |||
Logger.error(e, s); | |||
} | |||
return null; | |||
@@ -1,7 +1,11 @@ | |||
import 'package:meta/meta.dart'; | |||
import 'package:equatable/equatable.dart'; | |||
@immutable | |||
abstract class CustomPageState {} | |||
abstract class CustomPageState extends Equatable{ | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 初始化 | |||
class CustomPageInitialState extends CustomPageState {} | |||
@@ -10,6 +14,8 @@ class CustomPageInitialState extends CustomPageState {} | |||
class CustomPageLoadedState extends CustomPageState { | |||
List<Map<String, dynamic>> model; | |||
CustomPageLoadedState({this.model}); | |||
@override | |||
List<Object> get props => [this.model]; | |||
} | |||
/// 刷新成功 | |||
@@ -0,0 +1,188 @@ | |||
import 'package:flutter/material.dart'; | |||
import 'package:pull_to_refresh/pull_to_refresh.dart'; | |||
import 'package:provider/provider.dart'; | |||
import 'package:flutter_bloc/flutter_bloc.dart'; | |||
import 'package:zhiying_base_widget/pages/main_page/notifier/main_page_notifier.dart'; | |||
import 'package:zhiying_base_widget/widgets/refresh/refresh_footer/refresh_footer.dart'; | |||
import 'package:zhiying_base_widget/widgets/refresh/refresh_header/refresh_header.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'bloc/custom_item_page_bloc.dart'; | |||
import 'bloc/custom_item_page_state.dart'; | |||
import 'bloc/custom_item_page_event.dart'; | |||
import 'bloc/custom_item_page_repository.dart'; | |||
/// | |||
/// 通用模块的分类导航下的子模块 | |||
/// | |||
/// | |||
class CustomItemPage extends StatelessWidget { | |||
final Map<String, dynamic> data; | |||
final int tabIndex; | |||
final String modPid; | |||
final String modId; | |||
CustomItemPage(this.data, this.tabIndex, this.modId, this.modPid); | |||
@override | |||
Widget build(BuildContext context) { | |||
return MultiProvider( | |||
providers: [ | |||
ChangeNotifierProvider.value(value: MainPageNotifier()), | |||
], | |||
child: BlocProvider<CustomItemPageBloc>( | |||
create: (_) => CustomItemPageBloc(CustomItemPageRepository(this.data, this.tabIndex, this.modId, this.modPid)), | |||
child: _CustomItemPageContainer( | |||
this.data, | |||
this.tabIndex, | |||
this.modId, | |||
this.modPid, | |||
key: UniqueKey(), | |||
), | |||
), | |||
); | |||
} | |||
} | |||
class _CustomItemPageContainer extends StatefulWidget { | |||
final Map<String, dynamic> data; | |||
final int tabIndex; | |||
final String modPid; | |||
final String modId; | |||
const _CustomItemPageContainer(this.data, this.tabIndex, this.modId, this.modPid, {Key key}) : super(key: key); | |||
@override | |||
__CustomItemPageContainerState createState() => __CustomItemPageContainerState(); | |||
} | |||
class __CustomItemPageContainerState extends State<_CustomItemPageContainer> with AutomaticKeepAliveClientMixin { | |||
@override | |||
bool get wantKeepAlive => true; | |||
ScrollController _controller; | |||
RefreshController _refreshController; | |||
/// 回到顶点 | |||
void _scrollTop() { | |||
// _controller.jumpTo(0); | |||
_controller.animateTo(0, duration: Duration(milliseconds: 200), curve: Curves.linear); | |||
} | |||
/// 初始化 | |||
void _initEvent() { | |||
BlocProvider.of<CustomItemPageBloc>(context).add(CustomItemPageInitEvent()); | |||
} | |||
/// 刷新 | |||
void _refreshEvent() { | |||
BlocProvider.of<CustomItemPageBloc>(context).add(CustomItemPageRefreshEvent()); | |||
} | |||
/// 下拉更多 | |||
void _loadEvent() { | |||
// BlocProvider.of<CustomItemPageBloc>(context).add(CustomItemPageLoadEvent()); | |||
Provider.of<MainPageNotifier>(context, listen: false).loadMore(); | |||
Future.delayed(Duration(seconds: 1),()=> _refreshController?.loadComplete()); | |||
} | |||
@override | |||
void initState() { | |||
_controller = ScrollController(); | |||
_refreshController = RefreshController(initialRefresh: false); | |||
_initEvent(); | |||
super.initState(); | |||
} | |||
@override | |||
void dispose() { | |||
_controller?.dispose(); | |||
_refreshController?.dispose(); | |||
super.dispose(); | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocConsumer<CustomItemPageBloc, CustomItemPageState>( | |||
listener: (context, state) {}, | |||
buildWhen: (prev, current) { | |||
if (current is CustomItemPageRefreshSuccessState) { | |||
_refreshController?.refreshCompleted(resetFooterState: true); | |||
return false; | |||
} | |||
if (current is CustomItemPageRefreshErrorState) { | |||
_refreshController?.refreshFailed(); | |||
return false; | |||
} | |||
if (current is CustomItemPageLoadSuccessState) { | |||
_refreshController?.loadComplete(); | |||
return false; | |||
} | |||
if (current is CustomItemPageLoadErrorState) { | |||
_refreshController?.loadNoData(); | |||
return false; | |||
} | |||
if (current is CustomItemPageErrorState) { | |||
return false; | |||
} | |||
return true; | |||
}, | |||
builder: (context, state) { | |||
if (state is CustomItemPageLoadedState) { | |||
if (EmptyUtil.isEmpty(state.model)) | |||
return _buildEmptyWidget(); | |||
else | |||
return _buildMainWidget(state.model); | |||
} | |||
if (state is CustomItemPageInitErrorState) { | |||
return _buildEmptyWidget(); | |||
} | |||
return _buildSkeletonWidget(); | |||
}, | |||
); | |||
} | |||
/// 有数据 | |||
Widget _buildMainWidget(final List<Map<String, dynamic>> model) { | |||
return MediaQuery.removePadding( | |||
context: context, | |||
removeTop: true, | |||
child: SmartRefresher( | |||
controller: _refreshController, | |||
enablePullDown: true, | |||
enablePullUp: true, | |||
onRefresh: _refreshEvent, | |||
onLoading: _loadEvent, | |||
header: RefreshHeader(), | |||
footer: RefreshFooter(), | |||
child: CustomScrollView( | |||
controller: _controller, | |||
slivers: _buildContentWidgets(model), | |||
), | |||
), | |||
); | |||
} | |||
/// 根据widget的modName生成视图 | |||
List<Widget> _buildContentWidgets(final List<Map<String, dynamic>> datas) { | |||
List<Widget> result = []; | |||
for (int i = 0; i < datas.length; i++) { | |||
WidgetModel item = WidgetModel.fromJson(Map<String, dynamic>.from(datas[i])); | |||
result.addAll(WidgetFactory.create( | |||
item.modName, | |||
isSliver: true, | |||
model: datas[i], | |||
)); | |||
} | |||
return result; | |||
} | |||
/// 空数据 | |||
Widget _buildEmptyWidget() { | |||
return Container(); | |||
} | |||
/// 骨架图 | |||
Widget _buildSkeletonWidget() { | |||
return Container(); | |||
} | |||
} |
@@ -1,17 +1,22 @@ | |||
import 'dart:convert'; | |||
import 'package:flutter/cupertino.dart'; | |||
import 'package:pull_to_refresh/pull_to_refresh.dart'; | |||
import 'package:tab_indicator_styler/tab_indicator_styler.dart'; | |||
import 'package:zhiying_base_widget/pages/custom_page/custom_item_page.dart'; | |||
import 'package:zhiying_base_widget/pages/main_page/notifier/main_page_bg_notifier.dart'; | |||
import 'package:zhiying_base_widget/widgets/custom/search/custom_search_widget.dart'; | |||
import 'package:zhiying_base_widget/widgets/empty/empty_widget.dart'; | |||
import 'package:zhiying_base_widget/widgets/refresh/refresh_header/refresh_header.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:provider/provider.dart'; | |||
import 'package:zhiying_comm/util/custom_sliver_persistent_header_delegate.dart'; | |||
import 'package:flutter_bloc/flutter_bloc.dart'; | |||
import 'bloc/custom_page_bloc.dart'; | |||
import 'bloc/custom_page_state.dart'; | |||
import 'bloc/custom_page_event.dart'; | |||
import 'bloc/custom_page_repository.dart'; | |||
import 'dart:ui'; | |||
import 'package:fluttertoast/fluttertoast.dart'; | |||
/// | |||
/// 通用模块页面 | |||
@@ -24,55 +29,39 @@ class CustomPage extends StatelessWidget { | |||
@override | |||
Widget build(BuildContext context) { | |||
// return NestedScrollDemoPage(); | |||
return BlocProvider<CustomPageBloc>( | |||
create: (_) => CustomPageBloc(CustomPageRepository(data: data))..add(CustomPageInitEvent()), | |||
child: _CommonPageContainer(), | |||
// ), | |||
return MultiProvider( | |||
providers: [ | |||
ChangeNotifierProvider.value(value: MainPageBgNotifier()), | |||
], | |||
child: BlocProvider<CustomPageBloc>( | |||
create: (_) => CustomPageBloc(CustomPageRepository(data: data))..add(CustomPageInitEvent()), | |||
child: _CommonPageContainer(data), | |||
// ), | |||
), | |||
); | |||
} | |||
} | |||
class _CommonPageContainer extends StatefulWidget { | |||
final Map<String, dynamic> data; | |||
_CommonPageContainer(this.data); | |||
@override | |||
__CommonPageContainerState createState() => __CommonPageContainerState(); | |||
} | |||
class __CommonPageContainerState extends State<_CommonPageContainer> with SingleTickerProviderStateMixin { | |||
ScrollController _controller; | |||
ScrollController _controller2; | |||
RefreshController _refreshController; | |||
TabController _tabController; | |||
bool _isEnded = false; | |||
/// 回到顶点 | |||
void _scrollTop() { | |||
// _controller.jumpTo(0); | |||
_controller.animateTo(0, duration: Duration(milliseconds: 200), curve: Curves.linear); | |||
} | |||
/// 刷新 | |||
void _onRefreshEvent() async { | |||
BlocProvider.of<CustomPageBloc>(context).add(CustomPageRefreshEvent()); | |||
} | |||
/// 加载更多 | |||
void _onLoadEvent() async {} | |||
@override | |||
void initState() { | |||
_controller = ScrollController(); | |||
_controller2 = ScrollController(); | |||
_refreshController = RefreshController(initialRefresh: false); | |||
_tabController = TabController(length: 10, vsync: this); | |||
super.initState(); | |||
} | |||
@override | |||
void dispose() { | |||
_controller?.dispose(); | |||
_refreshController?.dispose(); | |||
_tabController?.dispose(); | |||
_controller2?.dispose(); | |||
super.dispose(); | |||
} | |||
@@ -87,244 +76,43 @@ class __CommonPageContainerState extends State<_CommonPageContainer> with Single | |||
return false; | |||
} | |||
if (current is CustomPageRefreshSuccessState) { | |||
_refreshController.refreshCompleted(resetFooterState: true); | |||
// _refreshController.refreshCompleted(resetFooterState: true); | |||
return false; | |||
} | |||
if (current is CustomPageRefreshErrorState) { | |||
_refreshController.refreshFailed(); | |||
// _refreshController.refreshFailed(); | |||
return false; | |||
} | |||
return true; | |||
}, | |||
builder: (context, state) { | |||
// if (state is CustomPageLoadedState) { | |||
// return _buildMain2Widget(); | |||
// } | |||
// if (state is CustomPageInitErrorState) { | |||
// return _buildMain2Widget(); | |||
// } | |||
// return _buildMain2Widget(); | |||
return _buildMain2Widget(); | |||
}, | |||
), | |||
); | |||
} | |||
/// 有数据 | |||
Widget _buildMainWidget() { | |||
double top = MediaQueryData.fromWindow(window).padding.top; | |||
return Scaffold( | |||
backgroundColor: HexColor.fromHex('#F9F9F9'), | |||
floatingActionButton: _buildFloatWidget(), | |||
floatingActionButtonLocation: _CustomFloatingActionButtonLocation(FloatingActionButtonLocation.endFloat, 0, -100), | |||
body: SmartRefresher( | |||
enablePullDown: true, | |||
enablePullUp: false, | |||
header: RefreshHeader( | |||
offsetY: top, | |||
), | |||
controller: _refreshController, | |||
onLoading: _onLoadEvent, | |||
onRefresh: _onRefreshEvent, | |||
child: CustomScrollView( | |||
key: UniqueKey(), | |||
controller: _controller, | |||
slivers: [ | |||
/// 标题 | |||
SliverAppBar( | |||
title: Text('标题'), | |||
centerTitle: true, | |||
pinned: true, | |||
), | |||
/// 有数据 | |||
if (state is CustomPageLoadedState) { | |||
if (EmptyUtil.isEmpty(state.model)) return _buildEmptyWidget(); | |||
return _buildMainWidget(state.model); | |||
} | |||
/// 轮播图 | |||
_buildSliverBanner(), | |||
/// 搜索 | |||
_buildSearch(), | |||
/// TAB BAR | |||
_buildTabbar(), | |||
SliverFillRemaining( | |||
child: TabBarView( | |||
controller: _tabController, | |||
children: List.generate(10, (index) => Container( | |||
height: double.infinity, | |||
width: double.infinity, | |||
color: Colors.white, | |||
child: MediaQuery.removePadding( | |||
context: context, | |||
removeTop: true, | |||
child: CustomScrollView( | |||
controller: _controller2, | |||
key: UniqueKey(), | |||
slivers: <Widget>[ | |||
_buildQucikEntry(), | |||
_buildSearch(), | |||
_buildSliverBanner(), | |||
_buildGoodsList(), | |||
], | |||
), | |||
), | |||
)), | |||
), | |||
) | |||
/// 初始化失败 | |||
if (state is CustomPageInitErrorState) { | |||
return _buildEmptyWidget(); | |||
} | |||
], | |||
), | |||
/// 骨架图 | |||
return _buildSkeletonWidget(); | |||
}, | |||
), | |||
); | |||
} | |||
/// 有数据2 | |||
Widget _buildMain2Widget(){ | |||
/// 有数据 | |||
Widget _buildMainWidget(List<Map<String, dynamic>> model) { | |||
return Scaffold( | |||
appBar: AppBar(title: Text('标题'),centerTitle: true,), | |||
appBar: _buildAppbar(model?.first), | |||
backgroundColor: HexColor.fromHex('#F9F9F9'), | |||
floatingActionButton: _buildFloatWidget(), | |||
// floatingActionButton: _buildFloatWidget(), | |||
floatingActionButtonLocation: _CustomFloatingActionButtonLocation(FloatingActionButtonLocation.endFloat, 0, -100), | |||
body: Column( | |||
children: <Widget>[ | |||
Container(height: 40, width: double.infinity, color: Colors.green,), | |||
Container( | |||
height: 40, | |||
width: double.infinity, | |||
color: Colors.red, | |||
child: TabBar( | |||
isScrollable: true, | |||
controller: _tabController, | |||
tabs: List.generate(10, (index) => Text('$index')), | |||
), | |||
), | |||
Expanded( | |||
child: TabBarView( | |||
controller: _tabController, | |||
children: List.generate(10, (index) => Container( | |||
height: double.infinity, | |||
width: double.infinity, | |||
color: Colors.white, | |||
child: MediaQuery.removePadding( | |||
context: context, | |||
removeTop: true, | |||
child: CustomScrollView( | |||
controller: _controller, | |||
key: UniqueKey(), | |||
slivers: <Widget>[ | |||
_buildQucikEntry(), | |||
_buildSearch(), | |||
_buildSliverBanner(), | |||
_buildSearch(title: '商品列表'), | |||
_buildGoodsList(), | |||
], | |||
), | |||
), | |||
)), | |||
), | |||
) | |||
], | |||
), | |||
); | |||
} | |||
/// tabBar | |||
Widget _buildTabbar(){ | |||
return SliverPersistentHeader( | |||
delegate: CustomSliverPersistentHeaderDelegate( | |||
max: 40, | |||
min: 40, | |||
child: Container( | |||
height: double.infinity, | |||
width: double.infinity, | |||
color: Colors.redAccent, | |||
child: TabBar( | |||
isScrollable: true, | |||
controller: _tabController, | |||
tabs: List.generate(10, (index) => Text('$index')), | |||
), | |||
), | |||
), | |||
pinned: true, | |||
); | |||
} | |||
/// 多眼导航 | |||
Widget _buildQucikEntry(){ | |||
return SliverToBoxAdapter( | |||
child: Container( | |||
alignment: Alignment.center, | |||
child: Text('多眼导航'), | |||
height: 70, | |||
width: double.infinity, | |||
color: Colors.green, | |||
), | |||
); | |||
} | |||
/// 搜索 | |||
Widget _buildSearch({String title}){ | |||
return SliverPersistentHeader( | |||
delegate: CustomSliverPersistentHeaderDelegate( | |||
max: 40, | |||
min: 40, | |||
child: Container( | |||
alignment: Alignment.center, | |||
child: Text(title ?? '搜索'), | |||
height: double.infinity, | |||
width: double.infinity, | |||
color: Colors.blueAccent, | |||
), | |||
), | |||
pinned: true, | |||
); | |||
} | |||
/// 商品列表 | |||
Widget _buildGoodsList(){ | |||
return SliverList( | |||
delegate: SliverChildBuilderDelegate((context, index) { | |||
return Container( | |||
height: 50, | |||
width: double.infinity, | |||
color: Colors.green[(index % 9 + 1) * 100], | |||
); | |||
}, childCount: 50)); | |||
} | |||
/// 轮播图 | |||
Widget _buildSliverBanner(){ | |||
return SliverPersistentHeader( | |||
delegate: CustomSliverPersistentHeaderDelegate( | |||
max: 140, | |||
min: 140, | |||
child: Container( | |||
child: Text('轮播图'), | |||
alignment: Alignment.center, | |||
height: double.infinity, | |||
width: double.infinity, | |||
color: Colors.yellowAccent, | |||
), | |||
), | |||
pinned: false, | |||
); | |||
} | |||
/// 悬浮按钮 | |||
Widget _buildFloatWidget() { | |||
return Visibility( | |||
visible: true, | |||
child: GestureDetector( | |||
onTap: ()=> _scrollTop(), | |||
behavior: HitTestBehavior.opaque, | |||
child: Container( | |||
height: 30, | |||
width: 30, | |||
color: Colors.redAccent, | |||
), | |||
body: Column( | |||
children: _buildFirstWidget(model), | |||
), | |||
); | |||
} | |||
@@ -350,8 +138,8 @@ class __CommonPageContainerState extends State<_CommonPageContainer> with Single | |||
onPressed: () => Navigator.maybePop(context), | |||
), | |||
title: Text( | |||
'爆款', | |||
style: TextStyle(color: HexColor.fromHex('#333333'), fontSize: 15, fontWeight: FontWeight.bold), | |||
'', | |||
style: TextStyle(color: HexColor.fromHex('#333333'), fontSize: 18, fontWeight: FontWeight.bold), | |||
), | |||
centerTitle: true, | |||
elevation: 0, | |||
@@ -383,8 +171,179 @@ class __CommonPageContainerState extends State<_CommonPageContainer> with Single | |||
], | |||
)); | |||
} | |||
/// 数据,生成第一层widget | |||
List<Widget> _buildFirstWidget(List<Map<String, dynamic>> model) { | |||
List<Widget> result = []; | |||
// 分类导航的key ⚠️ 这里先写成Test 后续要改 | |||
const String CATEGORY_KEY = 'category_test'; | |||
// 判断是否有分类导航 | |||
// 判断最后一个是否属于分类导航,如果属于,则有分类导航,如果不是,则无分类导航 | |||
bool haveCategory = !EmptyUtil.isEmpty(model?.last) && model.last.containsKey('mod_name') && model.last['mod_name'] == CATEGORY_KEY; | |||
int endIndexLength = model.length; | |||
// 如果没有分类导航,则取分类导航之上的所有mod | |||
if (!haveCategory) { | |||
for (int i = 0; i < model.length; i++) { | |||
Map<String, dynamic> item = model[i]; | |||
if (item['mod_name'] == CATEGORY_KEY) { | |||
endIndexLength = (i + 1); | |||
break; | |||
} | |||
} | |||
} | |||
for (int i = 0; i < endIndexLength; i++) { | |||
WidgetModel item = WidgetModel.fromJson(Map<String, dynamic>.from(model[i])); | |||
// last model | |||
if (i == endIndexLength - 1) { | |||
result.addAll(_buildTabBar(model[i])); | |||
break; | |||
} | |||
// appBar 无需在这里添加 | |||
if (item.modName.contains('appbar')) { | |||
continue; | |||
} | |||
result.addAll(WidgetFactory.create( | |||
item.modName, | |||
isSliver: false, | |||
model: model[i], | |||
)); | |||
} | |||
return result; | |||
} | |||
/// appbar | |||
Widget _buildAppbar(final Map<String, dynamic> model) { | |||
if (EmptyUtil.isEmpty(model)) return null; | |||
String mobName = model['mod_name']; | |||
if (!mobName.contains('_appbar')) return null; | |||
Map<String, dynamic> data = Map<String, dynamic>(); | |||
try { | |||
data = jsonDecode(model['data']); | |||
} catch (e, s) { | |||
Logger.warn(e, s); | |||
} | |||
String parentTitle = !EmptyUtil.isEmpty(widget?.data) ? widget?.data['title_1'] ?? '' : ''; | |||
return AppBar( | |||
backgroundColor: HexColor.fromHex(null != data ? data['app_bar_bg_color'] ?? '#FFFFFF' : '#FFFFFF'), | |||
brightness: Brightness.light, | |||
leading: IconButton( | |||
icon: Icon( | |||
Icons.arrow_back_ios, | |||
size: 22, | |||
color: HexColor.fromHex('#333333'), | |||
), | |||
onPressed: () => Navigator.maybePop(context), | |||
), | |||
title: Text( | |||
null != data && data.containsKey('app_bar_name') ? data['app_bar_name'] != '自定义页面' ? data['app_bar_name'] : parentTitle : parentTitle, | |||
style: TextStyle( | |||
color: HexColor.fromHex(null != data ? data['app_bar_name_color'] ?? '#333333' : '#333333'), | |||
fontSize: 16, | |||
fontWeight: FontWeight.bold, | |||
), | |||
), | |||
centerTitle: true, | |||
elevation: 0, | |||
); | |||
} | |||
/// tabBar | |||
List<Widget> _buildTabBar(final Map<String, dynamic> model) { | |||
Map<String, dynamic> data = Map<String, dynamic>(); | |||
List<Map<String, dynamic>> listStyle = []; | |||
List<Widget> result = []; | |||
try { | |||
data = jsonDecode(model['data']); | |||
listStyle = List.from(data['list_style']); | |||
} catch (e, s) { | |||
Logger.warn(e, s); | |||
} | |||
// 1、导航栏没开启的情况 传null进去进行获取没开启导航栏的widget集合 | |||
if (EmptyUtil.isEmpty(listStyle)) { | |||
result.add(Expanded( | |||
child: CustomItemPage(null, 0, model['mod_id']?.toString() ?? null, model['mod_pid']?.toString() ?? null), | |||
)); | |||
return result; | |||
} | |||
// 2、导航栏开启的情况 | |||
if (listStyle.length > 0) { | |||
// tabContorller 初始化 | |||
if (null == _tabController || _tabController.length != listStyle.length) { | |||
_tabController = new TabController(length: listStyle.length, vsync: this); | |||
} | |||
result.add(Container( | |||
height: 40, | |||
width: double.infinity, | |||
color: HexColor.fromHex('#FFFFFF'), | |||
child: TabBar( | |||
controller: _tabController, | |||
isScrollable: /*listStyle.length <= 5 ? false : */ true, | |||
labelColor: HexColor.fromHex('#FF4242'), | |||
labelStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), | |||
unselectedLabelColor: HexColor.fromHex('#999999'), | |||
unselectedLabelStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), | |||
indicatorSize: TabBarIndicatorSize.label, | |||
indicator: MaterialIndicator( | |||
color: HexColor.fromHex('#FF4242'), | |||
bottomLeftRadius: 1.25, | |||
topLeftRadius: 1.25, | |||
topRightRadius: 1.25, | |||
bottomRightRadius: 1.25, | |||
height: 2.5, | |||
horizontalPadding: 5, | |||
), | |||
tabs: listStyle.map((e) => Text(e['name'])).toList(), | |||
), | |||
)); | |||
// 最后添加TabBarView | |||
result.add(Expanded( | |||
child: TabBarView( | |||
controller: _tabController, | |||
children: _buildTabBarViewChildren(listStyle, model['mod_id']?.toString(), model['mod_pid']?.toString()), | |||
), | |||
)); | |||
} | |||
return result; | |||
} | |||
/// 返回TabBarView的视图 | |||
List<Widget> _buildTabBarViewChildren(final List<Map<String, dynamic>> listStyle, final String modId, final String modPid) { | |||
List<Widget> result = []; | |||
for (int i = 0; i < listStyle.length; i++) { | |||
result.add(CustomItemPage(listStyle[i], i, modId, modPid)); | |||
} | |||
return result; | |||
} | |||
// /// 悬浮按钮 | |||
// Widget _buildFloatWidget() { | |||
// return Visibility( | |||
// visible: true, | |||
// child: GestureDetector( | |||
// onTap: () => _scrollTop(), | |||
// behavior: HitTestBehavior.opaque, | |||
// child: Container( | |||
// height: 30, | |||
// width: 30, | |||
// child: Icon(Icons.arrow_upward), | |||
// ), | |||
// ), | |||
// ); | |||
// } | |||
} | |||
/// 回到顶部的icon | |||
class _CustomFloatingActionButtonLocation extends FloatingActionButtonLocation { | |||
FloatingActionButtonLocation location; | |||
double offsetX; // X方向的偏移量 | |||
@@ -8,6 +8,7 @@ import 'package:zhiying_base_widget/pages/goods_details_page/bloc/goods_details_ | |||
import 'package:zhiying_base_widget/pages/goods_details_page/bloc/goods_details_page_repository.dart'; | |||
import 'package:zhiying_base_widget/pages/goods_details_page/goods_details_page_sk.dart'; | |||
import 'package:zhiying_base_widget/pages/goods_details_page/notifier/goods_details_page_notifier.dart'; | |||
import 'package:zhiying_base_widget/pages/main_page/notifier/main_page_notifier.dart'; | |||
import 'package:zhiying_base_widget/widgets/goods_details/appbar/goods_details_appbar_widget.dart'; | |||
import 'package:zhiying_base_widget/widgets/goods_details/footer/goods_details_footer_widget.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
@@ -35,7 +36,7 @@ class _GoodsDetailsPageState extends State<GoodsDetailsPage> { | |||
return MultiProvider( | |||
providers: [ | |||
/// 滑动通知 | |||
ChangeNotifierProvider.value(value: GoodsDetailsPageNotifier()), | |||
ChangeNotifierProvider.value(value: MainPageNotifier()), | |||
ChangeNotifierProvider.value(value: GoodsDetailsAppBarColorNotifier()), | |||
], | |||
child: BlocProvider<GoodsDetailsPageBloc>( | |||
@@ -83,10 +84,10 @@ class _GoodsDetailsContainerState extends State<GoodsDetailsContainer> { | |||
if (_controller.offset >= _controller.position.maxScrollExtent && !_isEnded) { | |||
// 滑动到底部 | |||
_isEnded = true; | |||
Provider.of<GoodsDetailsPageNotifier>(context, listen: false).loadMore(); | |||
Provider.of<MainPageNotifier>(context, listen: false).loadMore(); | |||
} else if (_controller.offset < _controller.position.maxScrollExtent && _isEnded) { | |||
_isEnded = false; | |||
Provider.of<GoodsDetailsPageNotifier>(context, listen: false).reset(); | |||
Provider.of<MainPageNotifier>(context, listen: false).reset(); | |||
} | |||
if (_controller.offset >= 0 && _controller.offset <= BANNER_HEIGHT) { | |||
@@ -1,16 +1,16 @@ | |||
import 'package:flutter/material.dart'; | |||
class GoodsDetailsPageNotifier with ChangeNotifier { | |||
bool scrollEnd = false; | |||
// 加载更多数据 | |||
void loadMore() { | |||
scrollEnd = true; | |||
notifyListeners(); | |||
} | |||
void reset() { | |||
scrollEnd = false; | |||
notifyListeners(); | |||
} | |||
} | |||
// class GoodsDetailsPageNotifier with ChangeNotifier { | |||
// bool scrollEnd = false; | |||
// | |||
// // 加载更多数据 | |||
// void loadMore() { | |||
// scrollEnd = true; | |||
// notifyListeners(); | |||
// } | |||
// | |||
// void reset() { | |||
// scrollEnd = false; | |||
// notifyListeners(); | |||
// } | |||
// } |
@@ -173,9 +173,9 @@ class _MainPageContainerState extends State<_MainPageContainer> { | |||
list.add(SliverToBoxAdapter( | |||
child: Container( | |||
height: 200, | |||
child: Center( | |||
child: Text('暂时无数据哦~'), | |||
), | |||
// child: Center( | |||
// child: Text('暂时无数据哦~'), | |||
// ), | |||
), | |||
)); | |||
} | |||
@@ -1,11 +1,9 @@ | |||
import 'package:flutter/material.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
/// | |||
/// 通用模块的tabbar | |||
/// 首页的骨架图 | |||
/// | |||
class CustomTabBarWidget extends StatelessWidget { | |||
class MainPageSkeleton extends StatelessWidget { | |||
@override | |||
Widget build(BuildContext context) { | |||
return Container(); |
@@ -265,16 +265,16 @@ class OfficalWxchat { | |||
} | |||
class OfficalWxchatList { | |||
String type; | |||
String index; | |||
String text; | |||
String subText; | |||
String wechatAccount; | |||
String qrcode; | |||
OfficalWxchatList({this.type, this.text, this.subText, this.wechatAccount, this.qrcode}); | |||
OfficalWxchatList({this.index, this.text, this.subText, this.wechatAccount, this.qrcode}); | |||
OfficalWxchatList.fromJson(Map<String, dynamic> json) { | |||
type = json['type']; | |||
index = json['index']; | |||
text = json['text']; | |||
subText = json['sub_text']; | |||
wechatAccount = json['wechat_account']; | |||
@@ -283,7 +283,7 @@ class OfficalWxchatList { | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
data['type'] = this.type; | |||
data['index'] = this.index; | |||
data['text'] = this.text; | |||
data['sub_text'] = this.subText; | |||
data['wechat_account'] = this.wechatAccount; | |||
@@ -30,6 +30,9 @@ import 'package:zhiying_base_widget/pages/team_page/team_page.dart'; | |||
import 'package:zhiying_base_widget/pages/webview/base_webview.dart'; | |||
import 'package:zhiying_base_widget/pages/wechat_teacher_page/wechat_teacher_page.dart'; | |||
import 'package:zhiying_base_widget/pages/withdraw_page/withdraw_page.dart'; | |||
import 'package:zhiying_base_widget/widgets/custom/multi_nav/custom_quick_entry.dart'; | |||
import 'package:zhiying_base_widget/widgets/custom/search/custom_search_widget.dart'; | |||
import 'package:zhiying_base_widget/widgets/custom/slide_banner/custom_slide_banner_creater.dart'; | |||
import 'package:zhiying_base_widget/widgets/goods_details/footer/goods_details_footer_widget.dart'; | |||
import 'package:zhiying_base_widget/widgets/goods_details/price/goods_details_price_widget.dart'; | |||
import 'package:zhiying_base_widget/widgets/goods_details/slide_banner/goods_details_slide_banner_widget.dart'; | |||
@@ -233,15 +236,14 @@ class BaseWidgetRegister { | |||
/// 可滚动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()); | |||
@@ -261,8 +263,7 @@ class BaseWidgetRegister { | |||
WidgetFactory.regist('search_index_host_keyword', | |||
DefaultWidgetCreater((model) => SearchHotTagWidget(model))); | |||
// // 历史搜索标签 | |||
WidgetFactory.regist('search_index_history', | |||
DefaultWidgetCreater((model) => SearchHistoryTagWidget(model))); | |||
WidgetFactory.regist('search_index_history', DefaultWidgetCreater((model) => SearchHistoryTagWidget(model))); | |||
/// ==================== 搜索结果页面 ==================== /// | |||
// 输入框 | |||
@@ -395,5 +396,16 @@ class BaseWidgetRegister { | |||
DefaultWidgetCreater((model) => HotRankTableBar(model))); | |||
WidgetFactory.regist('hot_rank_tab_view', | |||
DefaultWidgetCreater((model) => HotRankingList(model))); | |||
/// ==================== 通用模块 ==================== /// | |||
// 搜索 | |||
WidgetFactory.regist('search_test', DefaultWidgetCreater((model) => CustomSearchWidget(model))); | |||
// 轮播广告位, 可滑动 | |||
WidgetFactory.regist('carousel_test', CustomSlideBannerCreater()); | |||
// 多眼导航,可滑动 | |||
WidgetFactory.regist('multi_nav_test', DefaultWidgetCreater((model) => CustomQuickEntry(model))); | |||
// 商品列表 | |||
WidgetFactory.regist('product_test', GoodsDetailCommendCreater()); | |||
} | |||
} |
@@ -1,16 +0,0 @@ | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'package:flutter/material.dart'; | |||
import 'custom_appbar_widget.dart'; | |||
class CustomAppBarCreater extends WidgetCreater { | |||
@override | |||
List<Widget> createWidgets(Map<String, dynamic> model) { | |||
return [CustomAppBarWidget(model)]; | |||
} | |||
@override | |||
bool isSliverChild() { | |||
return true; | |||
} | |||
} |
@@ -1,45 +0,0 @@ | |||
import 'dart:convert'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
/// | |||
/// 通用模块的AppBar Widget | |||
/// | |||
class CustomAppBarWidget extends StatelessWidget { | |||
final Map<String, dynamic> data; | |||
Map<String, dynamic> model; | |||
CustomAppBarWidget(this.data, {Key key}) : super(key: key) { | |||
try { | |||
var dataItem = data['data']; | |||
if (!EmptyUtil.isEmpty(dataItem)) { | |||
model = dataItem is Map ? dataItem : dataItem is String ? jsonDecode(dataItem) : null; | |||
} | |||
} catch (e, s) { | |||
Logger.error(e, s); | |||
} | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return SliverAppBar( | |||
leading: IconButton( | |||
icon: Icon( | |||
Icons.arrow_back_ios, | |||
size: 22, | |||
color: HexColor.fromHex( '#333333'), | |||
), | |||
onPressed: () => Navigator.maybePop(context), | |||
), | |||
title: Text( | |||
'自定义页面', | |||
style: TextStyle( | |||
fontSize: 15, | |||
color: HexColor.fromHex('#333333'), | |||
fontWeight: FontWeight.bold, | |||
), | |||
), | |||
); | |||
} | |||
} |
@@ -0,0 +1,4 @@ | |||
export 'custom_quick_entry_bloc.dart'; | |||
export 'custom_quick_entry_event.dart'; | |||
export 'custom_quick_entry_state.dart'; | |||
export 'custom_quick_entry_repository.dart'; |
@@ -0,0 +1,50 @@ | |||
import 'dart:async'; | |||
import 'dart:math'; | |||
import 'package:bloc/bloc.dart'; | |||
import 'package:flutter/cupertino.dart'; | |||
import 'package:zhiying_comm/util/empty_util.dart'; | |||
import './bloc.dart'; | |||
class CustomQuickEntryBloc extends Bloc<CustomQuickEntryEvent, CustomQuickEntryState> { | |||
CustomQuickEntryRepository repository; | |||
CustomQuickEntryBloc({@required this.repository}); | |||
@override | |||
CustomQuickEntryState get initialState => InitialCustomQuickEntryState(); | |||
@override | |||
Stream<CustomQuickEntryState> mapEventToState(CustomQuickEntryEvent event) async* { | |||
/// 初始化 | |||
if (event is CustomQuickEntryInitEvent) { | |||
yield* _mapHomeQuickEntryInitToState(event); | |||
} | |||
} | |||
/// 初始化 | |||
Stream<CustomQuickEntryState> _mapHomeQuickEntryInitToState(CustomQuickEntryInitEvent event) async* { | |||
/// 获取父页面传进来的数据 | |||
var parentData = await repository.fetchPreantData(event: event); | |||
if (!EmptyUtil.isEmpty(parentData)) { | |||
yield CustomQuickEntryLoadedState(model: parentData); | |||
} else { | |||
yield CustomQuickEntryErrorState(); | |||
} | |||
// /// 获取本地缓存数据 | |||
// var cached = await repository.fetchCachedData(event: event); | |||
// if (!EmptyUtil.isEmpty(cached)) { | |||
// yield CustomQuickEntryCachedState(model: cached); | |||
// } | |||
// | |||
// /// 获取网络的数据 | |||
// var result = await repository.fetchData(event: event); | |||
// if (!EmptyUtil.isEmpty(result)) { | |||
// yield CustomQuickEntryLoadedState(model: result); | |||
// } else { | |||
// yield CustomQuickEntryErrorState(); | |||
// } | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
import 'package:equatable/equatable.dart'; | |||
abstract class CustomQuickEntryEvent extends Equatable { | |||
const CustomQuickEntryEvent(); | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 初始事件 | |||
class CustomQuickEntryInitEvent extends CustomQuickEntryEvent { | |||
final Map<String, dynamic> model; | |||
const CustomQuickEntryInitEvent({this.model}); | |||
@override | |||
List<Object> get props => [this.model]; | |||
} |
@@ -0,0 +1,75 @@ | |||
import 'dart:convert'; | |||
import 'package:flutter/cupertino.dart'; | |||
import 'package:zhiying_base_widget/widgets/custom/multi_nav/model/custom_quick_entry_model.dart'; | |||
import 'package:zhiying_comm/util/net_util.dart'; | |||
import 'package:zhiying_comm/util/empty_util.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'custom_quick_entry_event.dart'; | |||
class CustomQuickEntryRepository { | |||
/// 获取数据 | |||
Future<CustomQuickEntryModel> fetchData({@required CustomQuickEntryInitEvent event}) async { | |||
try { | |||
var result = await NetUtil.post('/api/v1/mod/${event.model['mod_id']}', | |||
params: { | |||
'ids': [event.model['mod_id']] | |||
}, | |||
cache: true, | |||
method: NetMethod.GET); | |||
if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { | |||
Map<String, dynamic> data = result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]; | |||
return _getHomeQuickEntryModel(data); | |||
} | |||
} catch (e, s) { | |||
Logger.error(e, s); | |||
} | |||
return null; | |||
} | |||
/// 获取父页面传进来的数据 | |||
Future<CustomQuickEntryModel> fetchPreantData({@required CustomQuickEntryInitEvent event}) async { | |||
try { | |||
String jsonStr = event.model['data']; | |||
return CustomQuickEntryModel.fromJson(json.decode(jsonStr)); | |||
} catch (e, s) { | |||
Logger.error(e, s); | |||
} | |||
return null; | |||
} | |||
/// 获取缓存数据 | |||
Future<CustomQuickEntryModel> fetchCachedData({@required CustomQuickEntryInitEvent event}) async { | |||
try { | |||
var result = await NetUtil.getRequestCachedData('/api/v1/mod/${event.model['mod_id']}', params: { | |||
'ids': [event.model['mod_id']] | |||
}); | |||
if (!EmptyUtil.isEmpty(result)) { | |||
return _getHomeQuickEntryModel(result); | |||
} | |||
} catch (e, s) { | |||
Logger.error(e, s); | |||
} | |||
return null; | |||
} | |||
CustomQuickEntryModel _getHomeQuickEntryModel(var data) { | |||
try { | |||
if (!EmptyUtil.isEmpty(data) && !EmptyUtil.isEmpty(data['data'])) { | |||
var jsonData = jsonDecode(data['data']); | |||
if (!EmptyUtil.isEmpty(jsonData)) { | |||
CustomQuickEntryModel model = CustomQuickEntryModel.fromJson(jsonData); | |||
if (null != model) { | |||
return model; | |||
} | |||
} | |||
} | |||
} catch (e) { | |||
Logger.log(e); | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
import 'package:equatable/equatable.dart'; | |||
import 'package:flutter/cupertino.dart'; | |||
import 'package:zhiying_base_widget/widgets/custom/multi_nav/model/custom_quick_entry_model.dart'; | |||
abstract class CustomQuickEntryState extends Equatable { | |||
const CustomQuickEntryState(); | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 初始化状态 | |||
class InitialCustomQuickEntryState extends CustomQuickEntryState { | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 加载数据完毕 | |||
class CustomQuickEntryLoadedState extends CustomQuickEntryState { | |||
final CustomQuickEntryModel model; | |||
const CustomQuickEntryLoadedState({@required this.model}); | |||
@override | |||
List<Object> get props => [this.model]; | |||
} | |||
/// 加载缓存数据 | |||
class CustomQuickEntryCachedState extends CustomQuickEntryState { | |||
final CustomQuickEntryModel model; | |||
const CustomQuickEntryCachedState({@required this.model}); | |||
@override | |||
List<Object> get props => [this.model]; | |||
} | |||
/// 加载数据出错 | |||
class CustomQuickEntryErrorState extends CustomQuickEntryState {} |
@@ -0,0 +1,18 @@ | |||
import 'package:flutter/material.dart'; | |||
/// | |||
/// 图片工具类,隔离build | |||
/// | |||
class CachedNetworkImageUtil extends StatelessWidget { | |||
Widget child; | |||
CachedNetworkImageUtil({this.child}); | |||
@override | |||
Widget build(BuildContext context) { | |||
return RepaintBoundary( | |||
child: child, | |||
); | |||
} | |||
} | |||
@@ -0,0 +1,373 @@ | |||
import 'package:flutter/cupertino.dart'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:flutter_bloc/flutter_bloc.dart'; | |||
import 'package:flutter_swiper/flutter_swiper.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'package:cached_network_image/cached_network_image.dart'; | |||
import 'custom_quick_entry_sk.dart'; | |||
import 'model/custom_quick_entry_model.dart'; | |||
import 'bloc/bloc.dart'; | |||
class CustomQuickEntry extends StatelessWidget { | |||
final Map<String, dynamic> model; | |||
const CustomQuickEntry(this.model); | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocProvider<CustomQuickEntryBloc>( | |||
create: (_) => CustomQuickEntryBloc(repository: CustomQuickEntryRepository())..add(CustomQuickEntryInitEvent(model: model)), | |||
child: _CustomQuickEntryContainer(model), | |||
); | |||
} | |||
} | |||
class _CustomQuickEntryContainer extends StatefulWidget { | |||
final Map<String, dynamic> model; | |||
_CustomQuickEntryContainer(this.model); | |||
@override | |||
__CustomQuickEntryContainerState createState() => __CustomQuickEntryContainerState(); | |||
} | |||
class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> { | |||
/// Icon点击事件 | |||
void _itemIconClick(ListStyle model) { | |||
print("item type = ${model.skipIdentifier}"); | |||
// Navigator.push(context, CupertinoPageRoute(builder: (_) => CommonPage(null))); | |||
RouterUtil.route(model, model.toJson(), context); | |||
} | |||
SwiperController _controller; | |||
@override | |||
void initState() { | |||
_controller = SwiperController(); | |||
super.initState(); | |||
} | |||
@override | |||
void dispose() { | |||
_controller?.dispose(); | |||
super.dispose(); | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocConsumer<CustomQuickEntryBloc, CustomQuickEntryState>( | |||
listener: (context, state) { | |||
}, | |||
buildWhen: (prev, current) { | |||
return true; | |||
}, | |||
builder: (context, state) { | |||
if (state is CustomQuickEntryCachedState) { | |||
return _getMainWidget(state.model); | |||
} | |||
if (state is CustomQuickEntryLoadedState) { | |||
return _getMainWidget(state.model); | |||
} | |||
if(state is CustomQuickEntryErrorState){ | |||
return Container(); | |||
} | |||
return CustomQuickEntrySkeleton(); | |||
}, | |||
); | |||
} | |||
Widget _getMainWidget(CustomQuickEntryModel model) { | |||
// 数据总数 | |||
int totalDataSize = model?.listStyle?.length ?? 0; | |||
// 展示的总行数 | |||
int totalRowSize = int.parse(model?.rowSize ?? '1'); | |||
// 展示的列数 | |||
int columSize = int.parse(model?.columnSize ?? '5'); | |||
// 图标的高度 | |||
double iconHeight = 40.0; | |||
// 标题的高度 | |||
double titleHeight = 20.0; | |||
// 子元素的高度 | |||
double itemHeight = iconHeight; | |||
// 如果有一级标题 | |||
if (!EmptyUtil.isEmpty(model?.isShowTitle) && model.isShowTitle == '1') { | |||
itemHeight = iconHeight + titleHeight; | |||
} | |||
//如果有二级标题 | |||
if (!EmptyUtil.isEmpty(model?.isShowSubTitle) && model.isShowSubTitle == '1') { | |||
itemHeight = iconHeight + titleHeight * 2; | |||
} | |||
// 进度条的边距 | |||
double barMargin = 15.0; | |||
// 总页数 | |||
int totalPage = totalDataSize % (totalRowSize * columSize) == 0 ? totalDataSize ~/ (totalRowSize * columSize) : (totalDataSize ~/ (totalRowSize * columSize)) + 1; | |||
// 总体高度 = 行数 * (子元素高度 + 边距高度) + 进度条的高度 | |||
double totalHeight = totalRowSize * (itemHeight + barMargin) + 4; | |||
if (!EmptyUtil.isEmpty(model?.pagination) && model.pagination != 'type_null' /*model.pagination_open == '0'*/) { | |||
totalHeight = totalRowSize * (itemHeight + barMargin); | |||
} | |||
return Container( | |||
color: HexColor.fromHex(widget?.model['bg_color']), | |||
child: Container( | |||
margin: EdgeInsets.only(top: 15, bottom: totalPage > 1 ? 15 : 0), | |||
height: totalHeight, | |||
// 总体高度 | |||
width: double.infinity, | |||
color: HexColor.fromHex(widget?.model['bg_color']), | |||
child: Swiper( | |||
controller: _controller, | |||
itemCount: totalPage, | |||
loop: false, | |||
itemBuilder: (context, index) { | |||
return Container( | |||
height: double.infinity, | |||
width: double.infinity, | |||
padding: const EdgeInsets.symmetric(horizontal: 12.5), | |||
child: _getPageWidget( | |||
iconHeight: iconHeight, | |||
titleHeight: titleHeight, | |||
totalDataSize: totalDataSize, | |||
totalPage: totalPage, | |||
currentPage: index, | |||
totalRowSize: totalRowSize, | |||
columSize: columSize, | |||
model: model, | |||
itemHeight: itemHeight, | |||
), | |||
); | |||
}, | |||
pagination: totalPage <= 1 ? null : _getSwiperPaginationContorl(model, totalPage), | |||
), | |||
), | |||
); | |||
} | |||
/// 页的数据 | |||
Widget _getPageWidget( | |||
{double titleHeight, double iconHeight, int totalPage, int currentPage, int columSize, int totalRowSize, int totalDataSize, CustomQuickEntryModel model, double itemHeight}) { | |||
List rowList = []; | |||
for (int i = 0; i < totalRowSize; i++) { | |||
rowList.add(i); | |||
} | |||
return Column( | |||
crossAxisAlignment: CrossAxisAlignment.start, | |||
mainAxisAlignment: MainAxisAlignment.start, | |||
children: rowList.map((currentRow) { | |||
return Container( | |||
padding: EdgeInsets.only(bottom: 15), | |||
width: double.infinity, | |||
child: _getRowWidget( | |||
titleHeight: titleHeight, | |||
iconHeight: iconHeight, | |||
totalPage: totalPage, | |||
currentPage: currentPage, | |||
columSize: columSize, | |||
totalRowSize: totalRowSize, | |||
totalDataSize: totalDataSize, | |||
model: model, | |||
currentRow: currentRow, | |||
itemHeight: itemHeight), | |||
); | |||
}).toList(), | |||
); | |||
} | |||
/// 行的数据 | |||
Widget _getRowWidget( | |||
{double titleHeight, | |||
double iconHeight, | |||
int totalPage, | |||
int currentPage, | |||
int columSize, | |||
int totalRowSize, | |||
int totalDataSize, | |||
CustomQuickEntryModel model, | |||
int currentRow, | |||
double itemHeight}) { | |||
List itemList = []; | |||
for (int i = 0; i < columSize; i++) { | |||
itemList.add(i); | |||
} | |||
return Row( | |||
mainAxisAlignment: MainAxisAlignment.spaceBetween, | |||
crossAxisAlignment: CrossAxisAlignment.center, | |||
children: itemList.map((currentIndex) { | |||
return _getColumWidget( | |||
titleHeight: titleHeight, | |||
iconHeight: iconHeight, | |||
totalPage: totalPage, | |||
currentPage: currentPage, | |||
totalDataSize: totalDataSize, | |||
columSize: columSize, | |||
totalRowSize: totalRowSize, | |||
model: model, | |||
currentRow: currentRow, | |||
currentColum: currentIndex, | |||
itemHeight: itemHeight, | |||
); | |||
}).toList(), | |||
); | |||
} | |||
/// item 的数据 | |||
Widget _getColumWidget({ | |||
double titleHeight, | |||
double iconHeight, | |||
int totalPage, | |||
int currentPage, | |||
int columSize, | |||
int totalRowSize, | |||
int totalDataSize, | |||
CustomQuickEntryModel model, | |||
int currentRow, | |||
int currentColum, | |||
double itemHeight, | |||
}) { | |||
// 当前index = 当前的页数+1 * 当前的行数 + 当前的列数 | |||
// int currentIndex = (currentPage + 1) * currentRow + currentColum + currentRow*columSize; | |||
// int currentIndex = currentPage != 0 ? currentPage * (columSize * totalRowSize) + columSize + currentRow * columSize : | |||
// currentColum + currentRow * columSize; | |||
// 当前元素的下表 = 当前的列数 + 当前的行数 * 列数 + 当前的页数 * 当前的行数 + 当前的列数 | |||
int currentIndex = currentColum + currentRow * columSize + currentPage * totalRowSize * columSize; | |||
// print('current Index sss = $currentIndex'); | |||
if (currentIndex >= totalDataSize) { | |||
return Container( | |||
height: itemHeight, | |||
width: 60, | |||
); | |||
} | |||
ListStyle item = model?.listStyle[currentIndex]; | |||
return GestureDetector( | |||
behavior: HitTestBehavior.opaque, | |||
onTap: () => _itemIconClick(item), | |||
child: Container( | |||
height: itemHeight, | |||
width: 60, | |||
// color: Colors.red, | |||
child: Column( | |||
children: <Widget>[ | |||
/// 图标 | |||
MyNetWorkImage(item.img), | |||
/// 一级标题 | |||
Visibility( | |||
visible: !EmptyUtil.isEmpty(model?.isShowTitle) && model.isShowTitle == '1', | |||
child: Padding( | |||
padding: const EdgeInsets.only(top: 5), | |||
child: Text( | |||
item?.title ?? '', | |||
style: TextStyle(fontSize: 10, color: HexColor.fromHex(model?.titleColor)), | |||
), | |||
), | |||
), | |||
/// 二级标题 | |||
Visibility( | |||
visible: !EmptyUtil.isEmpty(model?.isShowSubTitle) && model.isShowSubTitle == '1', | |||
child: Padding( | |||
padding: const EdgeInsets.only(top: 5), | |||
child: Text( | |||
item?.subTitle ?? '', | |||
style: TextStyle(fontSize: 10, color: HexColor.fromHex(model?.subTitleColor)), | |||
), | |||
), | |||
) | |||
], | |||
), | |||
), | |||
); | |||
} | |||
/// 进度条 | |||
SwiperPagination _getSwiperPaginationContorl(CustomQuickEntryModel model, int pageCount) { | |||
if (EmptyUtil.isEmpty(model?.pagination) || model.pagination == 'type_null' /*model.pagination_open == '0'*/) { | |||
return null; | |||
} | |||
if (model.pagination == 'type_point') { | |||
// 点点点进度条 | |||
return _swiperPaginationDot(model); | |||
} else { | |||
// 自定义进度条 | |||
return _swiperCustomPagination(pageCount); | |||
} | |||
} | |||
// 进度条 圆形 | |||
SwiperPagination _swiperPaginationDot(CustomQuickEntryModel model) { | |||
return SwiperPagination( | |||
margin: const EdgeInsets.only(), | |||
builder: DotSwiperPaginationBuilder(color: HexColor.fromHex(model?.paginationUnselectColor), activeColor: HexColor.fromHex(model?.paginationSelectColor), size: 8, activeSize: 8)); | |||
} | |||
// 自定义进度条 条形 | |||
SwiperPlugin _swiperCustomPagination(int pageCount) { | |||
List list = []; | |||
for (int i = 0; i < pageCount; i++) { | |||
list.add(i); | |||
} | |||
return SwiperCustomPagination(builder: (BuildContext context, SwiperPluginConfig config) { | |||
return Align( | |||
alignment: Alignment(0.0, 1), | |||
child: Row( | |||
mainAxisAlignment: MainAxisAlignment.center, | |||
crossAxisAlignment: CrossAxisAlignment.center, | |||
children: list.map((index) { | |||
var borderRadius; | |||
if (index == 0) { | |||
borderRadius = BorderRadius.only(topLeft: Radius.circular(2), bottomLeft: Radius.circular(2)); | |||
} | |||
if (index == list.length - 1) { | |||
borderRadius = BorderRadius.only(topRight: Radius.circular(2), bottomRight: Radius.circular(2)); | |||
} | |||
if (index == config.activeIndex) { | |||
borderRadius = BorderRadius.all(Radius.circular(2)); | |||
} | |||
return Container( | |||
height: 4, | |||
width: 25, | |||
decoration: BoxDecoration(borderRadius: borderRadius, color: index == config.activeIndex ? HexColor.fromHex('#FF4242') : HexColor.fromHex('#FFFFFF')), | |||
); | |||
}).toList(), | |||
), | |||
); | |||
}); | |||
} | |||
} | |||
/// | |||
/// 图片build 优化 | |||
/// | |||
class MyNetWorkImage extends StatelessWidget { | |||
final String imgUrl; | |||
final double width; | |||
const MyNetWorkImage(this.imgUrl, {this.width = 40}); | |||
@override | |||
Widget build(BuildContext context) { | |||
return RepaintBoundary( | |||
child: CachedNetworkImage( | |||
width: width, | |||
imageUrl: imgUrl, | |||
), | |||
); | |||
} | |||
} |
@@ -0,0 +1,20 @@ | |||
import 'package:flutter/src/widgets/framework.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'custom_quick_entry.dart'; | |||
import 'custom_quick_entry_sk.dart'; | |||
/// | |||
/// 快速入口 | |||
/// | |||
class CustomQuickEntryCreater extends WidgetCreater { | |||
@override | |||
List<Widget> createSkeleton(Map<String, dynamic> model) { | |||
return [CustomQuickEntrySkeleton()]; | |||
} | |||
@override | |||
List<Widget> createWidgets(Map<String, dynamic> model) { | |||
return [CustomQuickEntry(model)]; | |||
} | |||
} |
@@ -0,0 +1,53 @@ | |||
import 'package:flutter/material.dart'; | |||
import 'package:shimmer/shimmer.dart'; | |||
class CustomQuickEntrySkeleton extends StatelessWidget { | |||
@override | |||
Widget build(BuildContext context) { | |||
return Container( | |||
width: double.infinity, | |||
height: 153, | |||
child: GridView.builder( | |||
physics: NeverScrollableScrollPhysics(), | |||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 5), | |||
itemBuilder: (context, index) { | |||
return CustomQuickEntrySkeletonItem( | |||
index: index, | |||
); | |||
}, | |||
itemCount: 10, | |||
), | |||
); | |||
} | |||
} | |||
class CustomQuickEntrySkeletonItem extends StatelessWidget { | |||
final int index; | |||
const CustomQuickEntrySkeletonItem({this.index}); | |||
@override | |||
Widget build(BuildContext context) { | |||
return Container( | |||
alignment: Alignment.center, | |||
child: Shimmer.fromColors( | |||
baseColor: Colors.grey[300], | |||
highlightColor: Colors.grey[100], | |||
child: Column( | |||
crossAxisAlignment: CrossAxisAlignment.center, | |||
mainAxisAlignment: MainAxisAlignment.center, | |||
children: <Widget>[ | |||
// 图片 | |||
Container( | |||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20)), | |||
height: 40, | |||
width: 40, | |||
), | |||
SizedBox(height: 5), | |||
// Container(height: 10.h, width: 40.w, color: Colors.white,), | |||
], | |||
), | |||
)); | |||
} | |||
} |
@@ -0,0 +1,187 @@ | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class CustomQuickEntryModel { | |||
String name; | |||
String desc; | |||
String pagination; | |||
String moduleType; | |||
String moduleKey; | |||
String isTopMargin; | |||
String isLeftRightMargin; | |||
String isShow; | |||
String topMargin; | |||
String leftRightMargin; | |||
String rowSize; | |||
String columnSize; | |||
String isShowSubTitle; | |||
String isShowTitle; | |||
String isShowCornerIcon; | |||
String isShowCategory; | |||
String paginationSelectColor; | |||
String paginationUnselectColor; | |||
List<ListStyle> listStyle; | |||
List<TypeList> typeList; | |||
int moduleKeyId; | |||
String titleColor; | |||
String subTitleColor; | |||
CustomQuickEntryModel({ | |||
this.name, | |||
this.desc, | |||
this.pagination, | |||
this.moduleType, | |||
this.moduleKey, | |||
this.isTopMargin, | |||
this.isLeftRightMargin, | |||
this.isShow, | |||
this.topMargin, | |||
this.leftRightMargin, | |||
this.rowSize, | |||
this.isShowSubTitle, | |||
this.isShowCornerIcon, | |||
this.isShowCategory, | |||
this.paginationSelectColor, | |||
this.paginationUnselectColor, | |||
this.listStyle, | |||
this.typeList, | |||
this.moduleKeyId, | |||
this.titleColor, | |||
this.subTitleColor, | |||
this.columnSize, | |||
this.isShowTitle, | |||
}); | |||
CustomQuickEntryModel.fromJson(Map<String, dynamic> json) { | |||
name = json['name']?.toString(); | |||
desc = json['desc']?.toString(); | |||
pagination = json['pagination']?.toString(); | |||
moduleType = json['module_type']?.toString(); | |||
moduleKey = json['module_key']?.toString(); | |||
isTopMargin = json['is_top_margin']?.toString(); | |||
isLeftRightMargin = json['is_left_right_margin']?.toString(); | |||
isShow = json['is_show']?.toString(); | |||
topMargin = json['top_margin']?.toString(); | |||
leftRightMargin = json['left_right_margin']?.toString(); | |||
rowSize = json['row_size']?.toString(); | |||
columnSize = json['column_size']?.toString() ?? '5'; | |||
isShowSubTitle = json['is_show_sub_title']?.toString(); | |||
isShowCornerIcon = json['is_show_corner_icon']?.toString(); | |||
isShowCategory = json['is_show_category']?.toString(); | |||
paginationSelectColor = json['pagination_select_color']?.toString(); | |||
paginationUnselectColor = json['pagination_unselect_color']?.toString(); | |||
if (json['list_style'] != null) { | |||
listStyle = new List<ListStyle>(); | |||
json['list_style'].forEach((v) { | |||
listStyle.add(new ListStyle.fromJson(v)); | |||
}); | |||
} | |||
if (json['type_list'] != null) { | |||
typeList = new List<TypeList>(); | |||
json['type_list'].forEach((v) { | |||
typeList.add(new TypeList.fromJson(v)); | |||
}); | |||
} | |||
moduleKeyId = json['module_key_id']; | |||
titleColor = json['title_color']; | |||
subTitleColor = json['sub_title_color']; | |||
isShowTitle = '1'; | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
data['name'] = this.name; | |||
data['desc'] = this.desc; | |||
data['pagination'] = this.pagination; | |||
data['module_type'] = this.moduleType; | |||
data['module_key'] = this.moduleKey; | |||
data['is_top_margin'] = this.isTopMargin; | |||
data['is_left_right_margin'] = this.isLeftRightMargin; | |||
data['is_show'] = this.isShow; | |||
data['top_margin'] = this.topMargin; | |||
data['left_right_margin'] = this.leftRightMargin; | |||
data['row_size'] = this.rowSize; | |||
data['column_size'] = this.columnSize; | |||
data['is_show_sub_title'] = this.isShowSubTitle; | |||
data['is_show_corner_icon'] = this.isShowCornerIcon; | |||
data['is_show_category'] = this.isShowCategory; | |||
data['pagination_select_color'] = this.paginationSelectColor; | |||
data['pagination_unselect_color'] = this.paginationUnselectColor; | |||
if (this.listStyle != null) { | |||
data['list_style'] = this.listStyle.map((v) => v.toJson()).toList(); | |||
} | |||
if (this.typeList != null) { | |||
data['type_list'] = this.typeList.map((v) => v.toJson()).toList(); | |||
} | |||
data['module_key_id'] = this.moduleKeyId; | |||
data['title_color'] = this.titleColor; | |||
data['sub_title_color'] = this.subTitleColor; | |||
data['is_show_title'] = this.isShowTitle; | |||
return data; | |||
} | |||
} | |||
class ListStyle extends SkipModel { | |||
String title; | |||
String img; | |||
String subTitle; | |||
String typeListKey; | |||
String rightIcon; | |||
String requiredLogin; | |||
String requiredTaobaoAuth; | |||
String skipIdentifier; | |||
ListStyle({ | |||
this.title, | |||
this.img, | |||
this.subTitle, | |||
this.typeListKey, | |||
this.rightIcon, | |||
this.requiredLogin, | |||
this.requiredTaobaoAuth, | |||
this.skipIdentifier, | |||
}); | |||
ListStyle.fromJson(Map<String, dynamic> json) { | |||
super.fromJson(json); | |||
title = json['title']; | |||
img = json['img']; | |||
subTitle = json['sub_title']; | |||
typeListKey = json['type_list_key']; | |||
rightIcon = json['right_icon']; | |||
requiredLogin = json['required_login']; | |||
requiredTaobaoAuth = json['required_taobao_auth']; | |||
skipIdentifier = json['skip_identifier']; | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = super.toJson(); | |||
data['title'] = this.title; | |||
data['img'] = this.img; | |||
data['sub_title'] = this.subTitle; | |||
data['type_list_key'] = this.typeListKey; | |||
data['right_icon'] = this.rightIcon; | |||
data['required_login'] = this.requiredLogin; | |||
data['required_taobao_auth'] = this.requiredTaobaoAuth; | |||
data['skip_identifier'] = this.skipIdentifier; | |||
return data; | |||
} | |||
} | |||
class TypeList { | |||
String name; | |||
String key; | |||
TypeList({this.name, this.key}); | |||
TypeList.fromJson(Map<String, dynamic> json) { | |||
name = json['name']; | |||
key = json['key']; | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
data['name'] = this.name; | |||
data['key'] = this.key; | |||
return data; | |||
} | |||
} |
@@ -1,13 +1,70 @@ | |||
import 'dart:convert'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:zhiying_base_widget/widgets/custom/search/model/custom_search_model.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
/// | |||
/// 通用模块的搜索栏 | |||
/// | |||
class CustomSearchWidget extends StatelessWidget { | |||
final Map<String, dynamic> data; | |||
CustomSearchModel model; | |||
CustomSearchWidget(this.data, {Key key}) : super(key: key) { | |||
try { | |||
model = CustomSearchModel.fromJson(jsonDecode(data['data'])); | |||
} catch (e, s) { | |||
Logger.warn(e, s); | |||
} | |||
} | |||
// 点击事件 | |||
void _onClickListener(BuildContext context, SkipModel skipModel) { | |||
RouterUtil.route(skipModel, skipModel.toJson(), context); | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return Container(); | |||
return _buildStyle1Widget(context); | |||
} | |||
/// 样式1 | |||
Widget _buildStyle1Widget(BuildContext context) { | |||
return GestureDetector( | |||
behavior: HitTestBehavior.opaque, | |||
onTap: () => _onClickListener(context, model?.listStyle?.searchCss), | |||
child: Container( | |||
width: double.infinity, | |||
decoration: BoxDecoration(color: HexColor.fromHex(model?.bgColor ?? '#FFFFFF')), | |||
padding: const EdgeInsets.symmetric(horizontal: 12.5, vertical: 4), | |||
child: Container( | |||
width: double.infinity, | |||
decoration: BoxDecoration( | |||
borderRadius: BorderRadius.circular(40), | |||
color: HexColor.fromHex(model?.listStyle?.searchCss?.bgColor ?? '#F9F9F9'), | |||
), | |||
padding: const EdgeInsets.symmetric(vertical: 6), | |||
child: Row( | |||
mainAxisAlignment: MainAxisAlignment.center, | |||
children: <Widget>[ | |||
/// 搜索按钮 | |||
CachedNetworkImage( | |||
imageUrl: model?.listStyle?.searchCss?.image ?? '', | |||
height: 20, | |||
width: 20, | |||
), | |||
const SizedBox(width: 7.5), | |||
/// 提示文字 | |||
Text( | |||
model?.listStyle?.searchCss?.text ?? '搜索更多优惠商品', | |||
style: TextStyle(fontSize: 14, color: HexColor.fromHex(model?.listStyle?.searchCss?.textColor ?? '#999999')), | |||
) | |||
], | |||
), | |||
), | |||
), | |||
); | |||
} | |||
} |
@@ -0,0 +1,157 @@ | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class CustomSearchModel { | |||
String name; | |||
String desc; | |||
String moduleType; | |||
String moduleKey; | |||
String isTopMargin; | |||
String isLeftRightMargin; | |||
String isShow; | |||
String topMargin; | |||
String leftRightMargin; | |||
String bgColor; | |||
ListStyle listStyle; | |||
CustomSearchModel({ | |||
this.name, | |||
this.desc, | |||
this.moduleType, | |||
this.moduleKey, | |||
this.isTopMargin, | |||
this.isLeftRightMargin, | |||
this.isShow, | |||
this.topMargin, | |||
this.leftRightMargin, | |||
this.bgColor, | |||
this.listStyle, | |||
}); | |||
CustomSearchModel.fromJson(Map<String, dynamic> json) { | |||
name = json['name']; | |||
desc = json['desc']; | |||
moduleType = json['module_type']; | |||
moduleKey = json['module_key']; | |||
isTopMargin = json['is_top_margin']; | |||
isLeftRightMargin = json['is_left_right_margin']; | |||
isShow = json['is_show']; | |||
topMargin = json['top_margin']; | |||
leftRightMargin = json['left_right_margin']; | |||
bgColor = json['bg_color']; | |||
listStyle = json['list_style'] != null ? new ListStyle.fromJson(json['list_style']) : null; | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
data['name'] = this.name; | |||
data['desc'] = this.desc; | |||
data['module_type'] = this.moduleType; | |||
data['module_key'] = this.moduleKey; | |||
data['is_top_margin'] = this.isTopMargin; | |||
data['is_left_right_margin'] = this.isLeftRightMargin; | |||
data['is_show'] = this.isShow; | |||
data['top_margin'] = this.topMargin; | |||
data['left_right_margin'] = this.leftRightMargin; | |||
data['bg_color'] = this.bgColor; | |||
if (this.listStyle != null) { | |||
data['list_style'] = this.listStyle.toJson(); | |||
} | |||
return data; | |||
} | |||
} | |||
class ListStyle { | |||
SearchCss searchCss; | |||
RightCss rightCss; | |||
ListStyle({this.searchCss, this.rightCss}); | |||
ListStyle.fromJson(Map<String, dynamic> json) { | |||
searchCss = json['search_css'] != null ? new SearchCss.fromJson(json['search_css']) : null; | |||
rightCss = json['right_css'] != null ? new RightCss.fromJson(json['right_css']) : null; | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
if (this.searchCss != null) { | |||
data['search_css'] = this.searchCss.toJson(); | |||
} | |||
if (this.rightCss != null) { | |||
data['right_css'] = this.rightCss.toJson(); | |||
} | |||
return data; | |||
} | |||
} | |||
class SearchCss extends SkipModel { | |||
String name; | |||
String text; | |||
String image; | |||
String bgColor; | |||
String textColor; | |||
String requiredLogin; | |||
String requiredTaobaoAuth; | |||
String skipIdentifier; | |||
SearchCss({this.name, this.text, this.image, this.bgColor, this.textColor, this.requiredLogin, this.requiredTaobaoAuth, this.skipIdentifier}); | |||
SearchCss.fromJson(Map<String, dynamic> json) { | |||
super.fromJson(json); | |||
name = json['name']; | |||
text = json['text']; | |||
image = json['image']; | |||
bgColor = json['bg_color']; | |||
textColor = json['text_color']; | |||
requiredLogin = json['required_login']; | |||
requiredTaobaoAuth = json['required_taobao_auth']; | |||
skipIdentifier = json['skip_identifier']; | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = super.toJson(); | |||
data['name'] = this.name; | |||
data['text'] = this.text; | |||
data['image'] = this.image; | |||
data['bg_color'] = this.bgColor; | |||
data['text_color'] = this.textColor; | |||
data['required_login'] = this.requiredLogin; | |||
data['required_taobao_auth'] = this.requiredTaobaoAuth; | |||
data['skip_identifier'] = this.skipIdentifier; | |||
return data; | |||
} | |||
} | |||
class RightCss extends SkipModel { | |||
String name; | |||
String text; | |||
String type; | |||
String image; | |||
String requiredLogin; | |||
String requiredTaobaoAuth; | |||
String skipIdentifier; | |||
RightCss({this.name, this.text, this.type, this.image, this.requiredLogin, this.requiredTaobaoAuth, this.skipIdentifier}); | |||
RightCss.fromJson(Map<String, dynamic> json) { | |||
super.fromJson(json); | |||
name = json['name']; | |||
text = json['text']; | |||
type = json['type']; | |||
image = json['image']; | |||
requiredLogin = json['required_login']; | |||
requiredTaobaoAuth = json['required_taobao_auth']; | |||
skipIdentifier = json['skip_identifier']; | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = super.toJson(); | |||
data['name'] = this.name; | |||
data['text'] = this.text; | |||
data['type'] = this.type; | |||
data['image'] = this.image; | |||
data['required_login'] = this.requiredLogin; | |||
data['required_taobao_auth'] = this.requiredTaobaoAuth; | |||
data['skip_identifier'] = this.skipIdentifier; | |||
return data; | |||
} | |||
} |
@@ -0,0 +1,5 @@ | |||
export 'custom_slide_banner_bloc.dart'; | |||
export 'custom_slide_banner_event.dart'; | |||
export 'custom_slide_banner_state.dart'; | |||
export 'custom_slide_banner_repository.dart'; | |||
export '../model/custom_slide_banner_model.dart'; |
@@ -0,0 +1,43 @@ | |||
import 'dart:async'; | |||
import 'package:bloc/bloc.dart'; | |||
import 'package:flutter/cupertino.dart'; | |||
import 'package:zhiying_comm/util/empty_util.dart'; | |||
import './bloc.dart'; | |||
class CustomSlideBannerBloc extends Bloc<CustomSlideBannerEvent, CustomSlideBannerState> { | |||
CustomSlideBannerRepository repository; | |||
CustomSlideBannerBloc({@required this.repository}); | |||
@override | |||
CustomSlideBannerState get initialState => InitialCustomSlideBannerState(); | |||
@override | |||
Stream<CustomSlideBannerState> mapEventToState(CustomSlideBannerEvent event) async* { | |||
final currentState = state; | |||
/// 初始化 | |||
if (event is CustomBannerInitEvent) { | |||
print('---------HomeBannerInitEvent---------'); | |||
yield* _mapInitEventToState(event); | |||
} | |||
} | |||
/// 初始化 | |||
Stream<CustomSlideBannerState> _mapInitEventToState(CustomBannerInitEvent event) async* { | |||
var parent = await repository.fetchPreantData(event.model); | |||
if (!EmptyUtil.isEmpty(parent)) { | |||
yield HomeSlideBannerLoadedState(datas: parent); | |||
return; | |||
} | |||
var cached = await repository.fetchCachedDate(id: event.model['mod_id']); | |||
if (!EmptyUtil.isEmpty(cached)) yield CustomSlideBannerCachedState(datas: cached); | |||
var param = await repository.fetchData(id: event.model['mod_id']); | |||
if (!EmptyUtil.isEmpty(param)) | |||
yield HomeSlideBannerLoadedState(datas: param); | |||
else | |||
yield HomeSlideBannerLoadError(); | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
import 'package:equatable/equatable.dart'; | |||
abstract class CustomSlideBannerEvent extends Equatable { | |||
const CustomSlideBannerEvent(); | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 初始事件 | |||
class CustomBannerInitEvent extends CustomSlideBannerEvent{ | |||
final Map<String, dynamic> model; | |||
const CustomBannerInitEvent({this.model}); | |||
} |
@@ -0,0 +1,45 @@ | |||
import 'dart:convert'; | |||
import 'package:flutter/cupertino.dart'; | |||
import 'package:zhiying_base_widget/widgets/custom/slide_banner/bloc/bloc.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class CustomSlideBannerRepository { | |||
/// 获取缓存数据 | |||
Future<CustomSlideBannerModel> fetchCachedDate({@required int id}) async { | |||
var cached = await NetUtil.getRequestCachedData('/api/v1/mod', params: { | |||
'ids': [id] | |||
}); | |||
if (!EmptyUtil.isEmpty(cached)) { | |||
try { | |||
return CustomSlideBannerModel.fromJson(cached); | |||
} catch (e) {} | |||
} | |||
return null; | |||
} | |||
/// 获取父页面传进来的数据 | |||
Future<CustomSlideBannerModel> fetchPreantData(@required Map<String, dynamic> model) async { | |||
try { | |||
if (!EmptyUtil.isEmpty(model)) { | |||
return CustomSlideBannerModel.fromJson(jsonDecode(model['data'])); | |||
} | |||
} catch (e) {} | |||
return null; | |||
} | |||
/// 获取数据 | |||
Future<CustomSlideBannerModel> fetchData({@required int id}) async { | |||
var params = await NetUtil.post('/api/v1/mod', | |||
params: { | |||
'ids': [id] | |||
}, | |||
cache: true); | |||
if (NetUtil.isSuccess(params) && !EmptyUtil.isEmpty(params[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { | |||
try { | |||
return CustomSlideBannerModel.fromJson(params[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||
} catch (e) {} | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
import 'package:equatable/equatable.dart'; | |||
import 'package:zhiying_base_widget/widgets/custom/slide_banner/model/custom_slide_banner_model.dart'; | |||
abstract class CustomSlideBannerState extends Equatable { | |||
const CustomSlideBannerState(); | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 初始化状态 | |||
class InitialCustomSlideBannerState extends CustomSlideBannerState { | |||
@override | |||
List<Object> get props => []; | |||
} | |||
/// 缓存数据 | |||
class CustomSlideBannerCachedState extends CustomSlideBannerState { | |||
CustomSlideBannerModel datas; | |||
CustomSlideBannerCachedState({this.datas}); | |||
@override | |||
List<Object> get props => [this.datas]; | |||
} | |||
/// 数据加载完毕状态 | |||
class HomeSlideBannerLoadedState extends CustomSlideBannerState { | |||
CustomSlideBannerModel datas; | |||
HomeSlideBannerLoadedState({this.datas}); | |||
HomeSlideBannerLoadedState copyWith({CustomSlideBannerModel newData}) { | |||
return HomeSlideBannerLoadedState( | |||
datas: newData ?? datas, | |||
); | |||
} | |||
@override | |||
List<Object> get props => [datas]; | |||
} | |||
/// 数据加载失败 | |||
class HomeSlideBannerLoadError extends CustomSlideBannerState {} |
@@ -0,0 +1,224 @@ | |||
import 'package:cached_network_image/cached_network_image.dart'; | |||
import 'package:flutter/cupertino.dart'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:flutter_bloc/flutter_bloc.dart'; | |||
import 'package:flutter_swiper/flutter_swiper.dart'; | |||
import 'package:provider/provider.dart'; | |||
import 'package:zhiying_base_widget/pages/main_page/notifier/main_page_bg_notifier.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'bloc/bloc.dart'; | |||
import 'bloc/custom_slide_banner_repository.dart'; | |||
import 'custom_slide_banner_sk.dart'; | |||
import 'model/custom_slide_banner_model.dart'; | |||
/// | |||
/// 可滑动 banner 轮播图 | |||
/// | |||
class CustomSlideBanner extends StatefulWidget { | |||
final Map<String, dynamic> model; | |||
const CustomSlideBanner(this.model, {Key key}) : super(key: key); | |||
@override | |||
_CustomSlideBannerState createState() => _CustomSlideBannerState(); | |||
} | |||
class _CustomSlideBannerState extends State<CustomSlideBanner> { | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocProvider<CustomSlideBannerBloc>( | |||
create: (_) => CustomSlideBannerBloc(repository: CustomSlideBannerRepository())..add(CustomBannerInitEvent(model: widget?.model)), | |||
child: CustomSlideBannerContainer(), | |||
); | |||
} | |||
} | |||
class CustomSlideBannerContainer extends StatefulWidget { | |||
@override | |||
_CustomSlideBannerContainerState createState() => _CustomSlideBannerContainerState(); | |||
} | |||
class _CustomSlideBannerContainerState extends State<CustomSlideBannerContainer> { | |||
/// 子元素点击事件 | |||
void _itemOnClick(IndexCarouselList model) { | |||
print('点击了 $model'); | |||
RouterUtil.route(model, model.toJson(), context); | |||
// Navigator.push(context, CupertinoPageRoute(builder: (_) => VipCenterPage(null))); | |||
} | |||
SwiperController _swiperController; | |||
@override | |||
void initState() { | |||
_swiperController = SwiperController(); | |||
super.initState(); | |||
} | |||
@override | |||
void dispose() { | |||
_swiperController?.dispose(); | |||
super.dispose(); | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocConsumer<CustomSlideBannerBloc, CustomSlideBannerState>( | |||
listener: (BuildContext context, CustomSlideBannerState state) { | |||
if (state is HomeSlideBannerLoadError) { | |||
print('数据加载出错'); | |||
} | |||
}, | |||
buildWhen: (previous, current) { | |||
/// 数据加载出错不进行build | |||
if (current is HomeSlideBannerLoadError) { | |||
return false; | |||
} | |||
return true; | |||
}, | |||
builder: (context, state) { | |||
print('currente state = $state'); | |||
if (state is HomeSlideBannerLoadedState) { | |||
return _getMainWidget(state.datas); | |||
} | |||
if (state is CustomSlideBannerCachedState) { | |||
return _getMainWidget(state.datas); | |||
} | |||
// 骨架屏 | |||
return CustomSlideBannerSkeleton(); | |||
}, | |||
); | |||
} | |||
Widget _getMainWidget(CustomSlideBannerModel datas) { | |||
Future.delayed(Duration.zero, () { | |||
Provider.of<MainPageBgNotifier>(context, listen: false).switchBg(Container( | |||
width: double.infinity, | |||
height: 200, | |||
color: Colors.redAccent, | |||
)); | |||
}); | |||
int size = datas?.indexCarouselList?.length ?? 0; | |||
return Container( | |||
margin: EdgeInsets.all(10), | |||
width: double.infinity, | |||
height: 140, | |||
child: Swiper( | |||
controller: _swiperController, | |||
itemBuilder: (BuildContext context, int index) { | |||
IndexCarouselList items = datas.indexCarouselList[index]; | |||
return Container( | |||
width: double.infinity, | |||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(7.5)), | |||
child: ClipRRect( | |||
borderRadius: BorderRadius.circular(7.5), | |||
child: CachedNetworkImage( | |||
imageUrl: items?.img ?? '', | |||
fit: BoxFit.cover, | |||
), | |||
), | |||
); | |||
}, | |||
itemCount: size, | |||
loop: size > 1 , | |||
autoplay: true, | |||
onTap: (index) => _itemOnClick(datas.indexCarouselList[index]), | |||
pagination: _getSwiperStyleByType(datas, size), | |||
onIndexChanged: (index) { | |||
// print('color = ${datas?.indexCarouselList[index]?.bgColor}'); | |||
Provider.of<MainPageBgNotifier>(context, listen: false).switchBg(Container( | |||
width: double.infinity, | |||
height: 200, | |||
color: HexColor.fromHex(datas?.indexCarouselList[index]?.bgColor), | |||
)); | |||
}, | |||
), | |||
); | |||
} | |||
/// 获取进度样式 | |||
SwiperPlugin _getSwiperStyleByType(CustomSlideBannerModel model, int pageCount) { | |||
// 一张图片就不滑动 | |||
if(pageCount <= 1){ | |||
return null; | |||
} | |||
if ('type_null' == model.pagination) { | |||
return null; | |||
} | |||
if ('type_number' == model.pagination) { | |||
return _getNumswiperPlugin(pageCount, model.paginationSelectColor, model.paginationUnselectColor); | |||
} | |||
if ('type_point' == model.pagination) { | |||
return _swiperCustomPaginationDito(pageCount, model.paginationSelectColor, model.paginationUnselectColor); | |||
} | |||
if ('type_bar' == model.pagination) { | |||
return _swiperCustomPagination(pageCount, model.paginationSelectColor, model.paginationUnselectColor); | |||
} | |||
return null; | |||
} | |||
/// 数字样式 | |||
SwiperPlugin _getNumswiperPlugin(int pageCount, String selectColor, String unselectColor) { | |||
return SwiperCustomPagination(builder: (BuildContext context, SwiperPluginConfig config) { | |||
return Align( | |||
alignment: Alignment(0.9, 0.9), | |||
child: Container( | |||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 18), | |||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(3), color: HexColor.fromHex('#80333333')), | |||
child: RichText( | |||
text: TextSpan(text: '${config.activeIndex + 1}', style: TextStyle(fontSize: 12, color: HexColor.fromHex(selectColor)), children: [ | |||
TextSpan(text: '/', style: TextStyle(fontSize: 12, color: HexColor.fromHex(unselectColor))), | |||
TextSpan(text: '$pageCount', style: TextStyle(fontSize: 12, color: HexColor.fromHex(unselectColor))), | |||
]), | |||
)), | |||
); | |||
}); | |||
} | |||
/// 自定义进度条 | |||
SwiperPlugin _swiperCustomPagination(int pageCount, String selectColor, String unselectColor) { | |||
List list = []; | |||
for (int i = 0; i < pageCount; i++) { | |||
list.add(i); | |||
} | |||
return SwiperCustomPagination(builder: (BuildContext context, SwiperPluginConfig config) { | |||
return Align( | |||
alignment: Alignment(0.0, 0.9), | |||
child: Row( | |||
mainAxisAlignment: MainAxisAlignment.center, | |||
crossAxisAlignment: CrossAxisAlignment.center, | |||
children: list.map((index) { | |||
var borderRadius; | |||
if (index == 0) { | |||
borderRadius = BorderRadius.only(topLeft: Radius.circular(2), bottomLeft: Radius.circular(2)); | |||
} | |||
if (index == list.length - 1) { | |||
borderRadius = BorderRadius.only(topRight: Radius.circular(2), bottomRight: Radius.circular(2)); | |||
} | |||
if (index == config.activeIndex) { | |||
borderRadius = BorderRadius.all(Radius.circular(2)); | |||
} | |||
return Container( | |||
height: 4, | |||
width: 25, | |||
decoration: BoxDecoration(borderRadius: borderRadius, color: index == config.activeIndex ? HexColor.fromHex(selectColor) : HexColor.fromHex(unselectColor)), | |||
); | |||
}).toList(), | |||
), | |||
); | |||
}); | |||
} | |||
/// 圆形进度条 | |||
SwiperPlugin _swiperCustomPaginationDito(int pageCount, String selectColor, String unselectColor) { | |||
return SwiperPagination( | |||
margin: const EdgeInsets.only(), builder: DotSwiperPaginationBuilder(color: HexColor.fromHex(unselectColor), activeColor: HexColor.fromHex(selectColor), size: 8, activeSize: 8)); | |||
} | |||
} |
@@ -0,0 +1,22 @@ | |||
import 'package:flutter/material.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'custom_slide_banner.dart'; | |||
import 'custom_slide_banner_sk.dart'; | |||
/// | |||
/// 可以滚动Banner | |||
/// | |||
class CustomSlideBannerCreater extends WidgetCreater { | |||
@override | |||
List<Widget> createSkeleton(Map<String, dynamic> model) { | |||
return [CustomSlideBannerSkeleton()]; | |||
} | |||
@override | |||
List<Widget> createWidgets(Map<String, dynamic> model) { | |||
return [ | |||
CustomSlideBanner(model), | |||
]; | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
import 'package:flutter/material.dart'; | |||
import 'package:shimmer/shimmer.dart'; | |||
class CustomSlideBannerSkeleton extends StatelessWidget { | |||
@override | |||
Widget build(BuildContext context) { | |||
return Container( | |||
width: double.infinity, | |||
height: 200, | |||
color: Colors.white, | |||
child: Shimmer.fromColors( | |||
baseColor: Colors.grey[300], | |||
highlightColor: Colors.grey[100], | |||
child: Container( | |||
margin: EdgeInsets.all(10), | |||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(10))), | |||
), | |||
), | |||
); | |||
} | |||
} |
@@ -0,0 +1,126 @@ | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class CustomSlideBannerModel { | |||
String name; | |||
String desc; | |||
String pagination; | |||
String moduleType; | |||
String moduleKey; | |||
String isTopMargin; | |||
String isLeftRightMargin; | |||
String isShow; | |||
String carouselType; | |||
String paginationSelectColor; | |||
String paginationUnselectColor; | |||
String barWidthHeightRatio; | |||
String bgWidthHeightRatio; | |||
String carouselTime; | |||
String topMargin; | |||
String leftRightMargin; | |||
List<IndexCarouselList> indexCarouselList; | |||
int moduleKeyId; | |||
CustomSlideBannerModel({ | |||
this.name, | |||
this.desc, | |||
this.pagination, | |||
this.moduleType, | |||
this.moduleKey, | |||
this.isTopMargin, | |||
this.isLeftRightMargin, | |||
this.isShow, | |||
this.carouselType, | |||
this.paginationSelectColor, | |||
this.paginationUnselectColor, | |||
this.barWidthHeightRatio, | |||
this.bgWidthHeightRatio, | |||
this.carouselTime, | |||
this.topMargin, | |||
this.leftRightMargin, | |||
this.indexCarouselList, | |||
this.moduleKeyId, | |||
}); | |||
CustomSlideBannerModel.fromJson(Map<String, dynamic> json) { | |||
name = json['name']; | |||
desc = json['desc']; | |||
pagination = json['pagination']; | |||
moduleType = json['module_type']; | |||
moduleKey = json['module_key']; | |||
isTopMargin = json['is_top_margin']; | |||
isLeftRightMargin = json['is_left_right_margin']; | |||
isShow = json['is_show']; | |||
carouselType = json['carousel_type']; | |||
paginationSelectColor = json['pagination_select_color']; | |||
paginationUnselectColor = json['pagination_unselect_color']; | |||
barWidthHeightRatio = json['bar_width_height_ratio']; | |||
bgWidthHeightRatio = json['bg_width_height_ratio']; | |||
carouselTime = json['carousel_time']; | |||
topMargin = json['top_margin']; | |||
leftRightMargin = json['left_right_margin']; | |||
if (json['index_carousel_list'] != null) { | |||
indexCarouselList = new List<IndexCarouselList>(); | |||
json['index_carousel_list'].forEach((v) { | |||
indexCarouselList.add(new IndexCarouselList.fromJson(v)); | |||
}); | |||
} | |||
moduleKeyId = json['module_key_id']; | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
data['name'] = this.name; | |||
data['desc'] = this.desc; | |||
data['pagination'] = this.pagination; | |||
data['module_type'] = this.moduleType; | |||
data['module_key'] = this.moduleKey; | |||
data['is_top_margin'] = this.isTopMargin; | |||
data['is_left_right_margin'] = this.isLeftRightMargin; | |||
data['is_show'] = this.isShow; | |||
data['carousel_type'] = this.carouselType; | |||
data['pagination_select_color'] = this.paginationSelectColor; | |||
data['pagination_unselect_color'] = this.paginationUnselectColor; | |||
data['bar_width_height_ratio'] = this.barWidthHeightRatio; | |||
data['bg_width_height_ratio'] = this.bgWidthHeightRatio; | |||
data['carousel_time'] = this.carouselTime; | |||
data['top_margin'] = this.topMargin; | |||
data['left_right_margin'] = this.leftRightMargin; | |||
if (this.indexCarouselList != null) { | |||
data['index_carousel_list'] = this.indexCarouselList.map((v) => v.toJson()).toList(); | |||
} | |||
data['module_key_id'] = this.moduleKeyId; | |||
return data; | |||
} | |||
} | |||
class IndexCarouselList extends SkipModel { | |||
String name; | |||
String img; | |||
String bgColor; | |||
String requiredLogin; | |||
String requiredTaobaoAuth; | |||
String skipIdentifier; | |||
IndexCarouselList({this.name, this.img, this.bgColor, this.requiredLogin, this.requiredTaobaoAuth, this.skipIdentifier}); | |||
IndexCarouselList.fromJson(Map<String, dynamic> json) { | |||
super.fromJson(json); | |||
name = json['name']; | |||
img = json['img']; | |||
bgColor = json['bg_color']; | |||
requiredLogin = json['required_login']; | |||
requiredTaobaoAuth = json['required_taobao_auth']; | |||
skipIdentifier = json['skip_identifier']; | |||
} | |||
Map<String, dynamic> toJson() { | |||
final Map<String, dynamic> data = super.toJson(); | |||
data['name'] = this.name; | |||
data['img'] = this.img; | |||
data['bg_color'] = this.bgColor; | |||
data['required_login'] = this.requiredLogin; | |||
data['required_taobao_auth'] = this.requiredTaobaoAuth; | |||
data['skip_identifier'] = this.skipIdentifier; | |||
return data; | |||
} | |||
} |
@@ -4,11 +4,13 @@ import 'package:flutter/cupertino.dart'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:provider/provider.dart'; | |||
import 'package:zhiying_base_widget/pages/goods_details_page/notifier/goods_details_page_notifier.dart'; | |||
import 'package:zhiying_base_widget/pages/main_page/notifier/main_page_notifier.dart'; | |||
import 'package:zhiying_base_widget/widgets/home/home_goods/bloc/home_goods_bloc.dart'; | |||
import 'package:zhiying_base_widget/widgets/home/home_goods/home_goods_item.dart'; | |||
import 'package:zhiying_base_widget/widgets/home/home_goods/home_goods_item_single.dart'; | |||
import 'package:zhiying_base_widget/widgets/home/home_goods/models/home_goods_model.dart'; | |||
import 'package:zhiying_base_widget/widgets/home/home_goods/models/home_goods_style_model.dart'; | |||
import 'package:zhiying_base_widget/widgets/home/home_goods/skeleton/home_goods_sk.dart'; | |||
import 'package:zhiying_comm/util/base_bloc.dart'; | |||
class GoodsDetailCommendList extends StatefulWidget { | |||
@@ -40,16 +42,13 @@ class _GoodsDetailCommendListContainer extends StatefulWidget { | |||
final Map<String, dynamic> data; | |||
final String provider; | |||
_GoodsDetailCommendListContainer(this.data, this.provider, {Key key}) | |||
: super(key: key); | |||
_GoodsDetailCommendListContainer(this.data, this.provider, {Key key}) : super(key: key); | |||
@override | |||
_GoodsDetailCommendListContainerState createState() => | |||
_GoodsDetailCommendListContainerState(); | |||
_GoodsDetailCommendListContainerState createState() => _GoodsDetailCommendListContainerState(); | |||
} | |||
class _GoodsDetailCommendListContainerState | |||
extends State<_GoodsDetailCommendListContainer> { | |||
class _GoodsDetailCommendListContainerState extends State<_GoodsDetailCommendListContainer> { | |||
HomeGoodsBloc _bloc; | |||
HomeGoodsStyleModel _style; | |||
@@ -70,8 +69,7 @@ class _GoodsDetailCommendListContainerState | |||
void didChangeDependencies() { | |||
super.didChangeDependencies(); | |||
print('didChangeDependencies'); | |||
bool isNeedLoadMore = | |||
Provider.of<GoodsDetailsPageNotifier>(context).scrollEnd; | |||
bool isNeedLoadMore = Provider.of<MainPageNotifier>(context).scrollEnd; | |||
if (isNeedLoadMore && widget.provider != '') { | |||
print('HomeGoods loadmore...'); | |||
_bloc.loadMore(widget.provider); | |||
@@ -83,6 +81,10 @@ class _GoodsDetailCommendListContainerState | |||
return StreamBuilder<List<HomeGoodsModel>>( | |||
stream: _bloc.outData, | |||
builder: (BuildContext context, AsyncSnapshot snapshot) { | |||
if (snapshot.data == null) { | |||
return SliverToBoxAdapter(child: HomeGoodsSkeleton()); | |||
} | |||
List<HomeGoodsModel> goods = snapshot.data; | |||
int column = 2; | |||
int count = ((goods?.length ?? 0) / column).ceil(); | |||
@@ -8,9 +8,10 @@ import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class GoodsDetailsRecommendWidget extends StatefulWidget { | |||
final Map<String, dynamic> data; | |||
// Map<String, dynamic> style = {}; | |||
HomeGoodsStyleModel styleModel; | |||
GoodsDetailsRecommendWidget(this.data, {Key key}) : super(key: key) { | |||
try { | |||
styleModel = HomeGoodsStyleModel.fromJson(convert.jsonDecode(data['data'])); | |||
@@ -47,36 +48,39 @@ class _GoodsDetailsRecommendWidgetState extends State<GoodsDetailsRecommendWidge | |||
} | |||
Widget _createTitle() { | |||
return Container( | |||
width: double.infinity, | |||
height: 50, | |||
child: Row( | |||
crossAxisAlignment: CrossAxisAlignment.center, | |||
mainAxisAlignment: MainAxisAlignment.center, | |||
children: <Widget>[ | |||
CachedNetworkImage( | |||
// imageUrl: _style.recListIcon ?? '', | |||
// imageUrl: widget?.style['rec_icon'] ?? '', | |||
imageUrl: widget?.styleModel?.recIcon ?? '', | |||
width: 15, | |||
height: 15, | |||
), | |||
Padding( | |||
padding: const EdgeInsets.only(left: 2.5), | |||
child: Text( | |||
// _style?.recListText ?? '', | |||
// widget?.style['title'] ?? '', | |||
widget?.styleModel?.title ?? '', | |||
style: TextStyle( | |||
// color: HexColor.fromHex(_style?.recListTextColor ?? 'FF4242'), | |||
// color: HexColor.fromHex(widget?.style['title_color'] ?? ''), | |||
color: HexColor.fromHex(widget?.styleModel?.titleColor ?? ''), | |||
fontSize: 14, | |||
), | |||
if (EmptyUtil.isEmpty(widget?.styleModel?.title)) | |||
return Container(); | |||
else | |||
return Container( | |||
width: double.infinity, | |||
height: 50, | |||
child: Row( | |||
crossAxisAlignment: CrossAxisAlignment.center, | |||
mainAxisAlignment: MainAxisAlignment.center, | |||
children: <Widget>[ | |||
CachedNetworkImage( | |||
// imageUrl: _style.recListIcon ?? '', | |||
// imageUrl: widget?.style['rec_icon'] ?? '', | |||
imageUrl: widget?.styleModel?.recIcon ?? '', | |||
width: 15, | |||
height: 15, | |||
), | |||
) | |||
], | |||
), | |||
); | |||
Padding( | |||
padding: const EdgeInsets.only(left: 2.5), | |||
child: Text( | |||
// _style?.recListText ?? '', | |||
// widget?.style['title'] ?? '', | |||
widget?.styleModel?.title ?? '', | |||
style: TextStyle( | |||
// color: HexColor.fromHex(_style?.recListTextColor ?? 'FF4242'), | |||
// color: HexColor.fromHex(widget?.style['title_color'] ?? ''), | |||
color: HexColor.fromHex(widget?.styleModel?.titleColor ?? ''), | |||
fontSize: 14, | |||
), | |||
), | |||
) | |||
], | |||
), | |||
); | |||
} | |||
} |
@@ -99,7 +99,7 @@ class _HomeGoodsContainerState extends State<_HomeGoodsContainer> { | |||
// } | |||
Loading.dismiss(); | |||
List<HomeGoodsModel> goods = snapshot.data; | |||
int column = int.tryParse(_style.listColumn); | |||
int column = int.tryParse(_style.listColumn ?? '1'); | |||
column = column <= 0 ? 1 : column; | |||
int count = ((goods?.length ?? 0) / column).ceil(); | |||
return SliverList( | |||
@@ -117,9 +117,15 @@ class _HomeSlideBannerContainerState extends State<HomeSlideBannerContainer> { | |||
IndexCarousel items = datas.index_carousel_list[index]; | |||
return Container( | |||
width: double.infinity, | |||
child: CachedNetworkImage( | |||
imageUrl: items?.img ?? '', | |||
fit: BoxFit.cover, | |||
decoration: BoxDecoration( | |||
borderRadius: BorderRadius.circular(7.5) | |||
), | |||
child: ClipRRect( | |||
borderRadius: BorderRadius.circular(7.5), | |||
child: CachedNetworkImage( | |||
imageUrl: items?.img ?? '', | |||
fit: BoxFit.cover, | |||
), | |||
), | |||
); | |||
}, | |||
@@ -11,6 +11,9 @@ import 'package:zhiying_base_widget/widgets/mine/mine_header/model/mine_profile_ | |||
import 'package:zhiying_comm/util/base_bloc.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
/// | |||
/// 我的页面:头部wiget | |||
/// | |||
class MineHeaderContainer extends StatefulWidget { | |||
final Map<String, dynamic> data; | |||
Map<String, dynamic> json; | |||