@@ -112,9 +112,9 @@ android { | |||
// 智夜生活 | |||
zhiying { | |||
applicationId "cn.zhios.zhiying" | |||
versionCode 32 | |||
versionCode 33 | |||
dimension "app" | |||
versionName '1.2.32' | |||
versionName '1.2.33' | |||
// 签名信息 | |||
signingConfig signingConfigs.zhiying | |||
} | |||
@@ -136,6 +136,7 @@ class __CustomItemPageContainerState extends State<_CustomItemPageContainer> wit | |||
}, | |||
builder: (context, state) { | |||
if (state is CustomItemPageLoadedState) { | |||
Logger.log('custom item page current state = ' + state?.toString()); | |||
if (EmptyUtil.isEmpty(state.model)) | |||
return _buildEmptyWidget(); | |||
else | |||
@@ -59,7 +59,7 @@ class _CommonPageContainer extends StatefulWidget { | |||
__CommonPageContainerState createState() => __CommonPageContainerState(); | |||
} | |||
class __CommonPageContainerState extends State<_CommonPageContainer> with SingleTickerProviderStateMixin { | |||
class __CommonPageContainerState extends State<_CommonPageContainer> with SingleTickerProviderStateMixin ,AutomaticKeepAliveClientMixin{ | |||
TabController _tabController; | |||
// 是否有AppBar | |||
@@ -418,6 +418,10 @@ class __CommonPageContainerState extends State<_CommonPageContainer> with Single | |||
} | |||
} | |||
} | |||
@override | |||
// TODO: implement wantKeepAlive | |||
bool get wantKeepAlive =>true; | |||
} | |||
/// 回到顶部的icon | |||
@@ -336,4 +336,5 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver, Ticker | |||
) | |||
])); | |||
} | |||
} |
@@ -110,6 +110,7 @@ class _MainPageContainerState extends State<_MainPageContainer> { | |||
return StreamBuilder<List<Map<String, dynamic>>>( | |||
stream: _bloc.outData, | |||
builder: (BuildContext context, AsyncSnapshot snapshot) { | |||
print("mainPageBuild"); | |||
List widgets = _createContent(context, snapshot.data ?? []); | |||
_refreshController.refreshCompleted(); | |||
Widget bgWidget = validateBgWidget(context, snapshot.data ?? []); | |||
@@ -35,7 +35,7 @@ class OrderContentBloc extends BlocBase { | |||
params['state'] = _state; | |||
NetUtil.request('/api/v1/order', method: NetMethod.POST, params: params, | |||
onCache: (data) { | |||
_parseData(data); | |||
//_parseData(data); | |||
}, onSuccess: (data) { | |||
_parseData(data); | |||
}); | |||
@@ -66,6 +66,6 @@ class OrderContentBloc extends BlocBase { | |||
return OrderModel.fromJson(Map<String, dynamic>.from(item)); | |||
}).toList()); | |||
_ordersController.add(_orders); | |||
_ordersController?.add(_orders); | |||
} | |||
} |
@@ -22,7 +22,7 @@ class OrderPageBloc extends BlocBase { | |||
void loadData(String skipIdentifier) async { | |||
NetUtil.request('/api/v1/mod/${skipIdentifier.toString()}', | |||
method: NetMethod.GET, onCache: (data) { | |||
_loadData(data); | |||
// _loadData(data); | |||
}, onSuccess: (data) { | |||
_loadData(data); | |||
}); | |||
@@ -20,6 +20,8 @@ class OrderModel { | |||
String confirmAt; | |||
String settleAt; | |||
String thumbnail; | |||
int priceType; | |||
String priceName; | |||
Map<String, dynamic> data; // 保存原始数据 | |||
OrderModel({ | |||
@@ -69,6 +71,8 @@ class OrderModel { | |||
confirmAt = json['confirm_at']; | |||
settleAt = json['settle_at']; | |||
thumbnail = json['thumbnail']; | |||
priceType=json['price_type']; | |||
priceName=json['price_name']; | |||
data = json; | |||
} | |||
@@ -1,3 +1,5 @@ | |||
import 'dart:async'; | |||
import 'package:event_bus/event_bus.dart'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:pull_to_refresh/pull_to_refresh.dart'; | |||
@@ -59,13 +61,15 @@ class _OrderContentContainerState extends State<OrderContentContainer> { | |||
OrderContentBloc _bloc; | |||
RefreshController _refreshController = | |||
RefreshController(initialRefresh: false); | |||
StreamSubscription streamSubscription; | |||
@override | |||
void initState() { | |||
_bloc = BlocProvider.of(context); | |||
_bloc.refresh(widget.state, widget.filter); | |||
widget.eventBus.on<int>().listen((index) { | |||
streamSubscription= widget.eventBus.on<int>().listen((index) { | |||
print('eventbus' + index.toString()); | |||
if (index == widget.index) { | |||
_bloc.refresh(widget.state, widget.filter); | |||
} | |||
@@ -73,6 +77,11 @@ class _OrderContentContainerState extends State<OrderContentContainer> { | |||
super.initState(); | |||
} | |||
@override | |||
void dispose() { | |||
streamSubscription.cancel(); | |||
super.dispose(); | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
@@ -1,3 +1,5 @@ | |||
import 'dart:io'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:flutter/services.dart'; | |||
import 'package:fluttertoast/fluttertoast.dart'; | |||
@@ -18,8 +20,7 @@ class OrderItemWidget extends StatelessWidget { | |||
margin: EdgeInsets.only(left: 12.5, right: 12.5, top: 4, bottom: 4), | |||
padding: EdgeInsets.all(10), | |||
width: double.infinity, | |||
decoration: BoxDecoration( | |||
borderRadius: BorderRadius.circular(7.5), color: Colors.white), | |||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(7.5), color: Colors.white), | |||
child: Column( | |||
children: <Widget>[ | |||
_createHeader(), | |||
@@ -48,21 +49,35 @@ class OrderItemWidget extends StatelessWidget { | |||
} | |||
List<OrderStateModel> states = style.list.orderState; | |||
OrderStateModel state = | |||
states.firstWhere((element) => element.type == model.state.toString()); | |||
OrderStateModel state = states.firstWhere((element) => element.type == model.state.toString()); | |||
return Padding( | |||
padding: const EdgeInsets.only(bottom: 8), | |||
child: Row( | |||
children: <Widget>[ | |||
Expanded( | |||
child: Text( | |||
type ?? '', | |||
maxLines: 1, | |||
style: TextStyle( | |||
fontSize: 12, | |||
fontWeight: FontWeight.bold, | |||
color: Color(0xff333333), | |||
), | |||
child: Row( | |||
children: <Widget>[ | |||
Text( | |||
type ?? '', | |||
maxLines: 1, | |||
style: TextStyle( | |||
fontSize: 12, | |||
fontWeight: FontWeight.bold, | |||
color: Color(0xff333333), | |||
), | |||
), | |||
model?.priceName == null | |||
? Container() | |||
: Container( | |||
decoration: BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(4)), | |||
margin: EdgeInsets.only(left: 4), | |||
padding: EdgeInsets.all(3), | |||
child: Text( | |||
model?.priceName ?? "", | |||
style: TextStyle(color: HexColor.fromHex("#FFFFFF"), fontSize: 9, height: 1), | |||
), | |||
) | |||
], | |||
), | |||
), | |||
Text( | |||
@@ -102,8 +117,7 @@ class OrderItemWidget extends StatelessWidget { | |||
_createTitle(), | |||
Row( | |||
children: <Widget>[ | |||
_creteText( | |||
'${style?.list?.textOrderNo ?? ''}${model?.ordId ?? ''}'), | |||
_creteText('${style?.list?.textOrderNo ?? ''}${model?.ordId ?? ''}'), | |||
GestureDetector( | |||
onTap: () { | |||
if (model?.ordId != null && model.ordId.length > 0) { | |||
@@ -132,10 +146,8 @@ class OrderItemWidget extends StatelessWidget { | |||
) | |||
], | |||
), | |||
_creteText( | |||
'${style?.list?.textOrderTime ?? ''}${model?.createAt ?? ''}'), | |||
_creteText( | |||
'${style?.list?.textFinishTime ?? ''}${model?.confirmAt ?? ''}'), | |||
_creteText('${style?.list?.textOrderTime ?? ''}${model?.createAt ?? ''}'), | |||
_creteText('${style?.list?.textFinishTime ?? ''}${model?.confirmAt ?? ''}'), | |||
], | |||
), | |||
) | |||
@@ -147,10 +159,7 @@ class OrderItemWidget extends StatelessWidget { | |||
List<InlineSpan> list = List(); | |||
String shop = ''; | |||
if (model?.provider != null && model.provider != '') { | |||
shop = style.filter.providerType | |||
.firstWhere((element) => element.type == model.provider) | |||
?.name ?? | |||
''; | |||
shop = style.filter.providerType.firstWhere((element) => element.type == model.provider)?.name ?? ''; | |||
} | |||
if (shop != '') { | |||
list.add(WidgetSpan( | |||
@@ -162,23 +171,17 @@ class OrderItemWidget extends StatelessWidget { | |||
style: TextStyle( | |||
fontSize: 9, | |||
height: 1, | |||
color: | |||
HexColor.fromHex(style.list.colorProviderFont ?? '#ffffff'), | |||
color: HexColor.fromHex(style.list.colorProviderFont ?? '#ffffff'), | |||
), | |||
), | |||
decoration: BoxDecoration( | |||
color: HexColor.fromHex(style.list.colorProviderBg ?? '#ffffff'), | |||
borderRadius: BorderRadius.circular(2.5)), | |||
decoration: BoxDecoration(color: HexColor.fromHex(style.list.colorProviderBg ?? '#ffffff'), borderRadius: BorderRadius.circular(2.5)), | |||
), | |||
)); | |||
} | |||
list.add( | |||
TextSpan( | |||
text: model?.itemTitle ?? '', | |||
style: TextStyle( | |||
fontSize: 15, | |||
color: Color(0xff333333), | |||
fontWeight: FontWeight.bold), | |||
style: TextStyle(fontSize: 15, color: Color(0xff333333), fontWeight: FontWeight.bold), | |||
), | |||
); | |||
return RichText( | |||
@@ -205,8 +208,7 @@ class OrderItemWidget extends StatelessWidget { | |||
Widget _createTips() { | |||
List<OrderStateModel> states = style.list.orderState; | |||
OrderStateModel state = | |||
states.firstWhere((element) => element.type == model.state.toString()); | |||
OrderStateModel state = states.firstWhere((element) => element.type == model.state.toString()); | |||
if (state == null || state.tips == null || state.tips == '') { | |||
return Container(); | |||
} | |||
@@ -214,9 +216,7 @@ class OrderItemWidget extends StatelessWidget { | |||
margin: EdgeInsets.only(top: 8), | |||
padding: EdgeInsets.only(left: 8, right: 8, top: 2, bottom: 2), | |||
width: double.infinity, | |||
decoration: BoxDecoration( | |||
color: HexColor.fromHex(style.list.colorTipsBg ?? '#f5f5f5'), | |||
borderRadius: BorderRadius.circular(4)), | |||
decoration: BoxDecoration(color: HexColor.fromHex(style.list.colorTipsBg ?? '#f5f5f5'), borderRadius: BorderRadius.circular(4)), | |||
child: Text( | |||
state?.tips ?? '', | |||
style: TextStyle(color: Color(0xff666666), fontSize: 10), | |||
@@ -77,7 +77,6 @@ class _SearchPageContianerState extends State<SearchPageContianer> { | |||
Widget build(BuildContext context) { | |||
//设置搜索bar应该选择哪个type的item | |||
Logger.log("数据2: " + json.encode(widget.data)); | |||
Logger.log("类型: " + widget.data['default_pvd']); | |||
if(!EmptyUtil.isEmpty(widget.data['default_pvd'])){ | |||
Provider.of<SearchTagNotifier>(context, listen: false).setType(widget.data['default_pvd']); | |||
} | |||
@@ -1,3 +1,4 @@ | |||
import 'dart:async'; | |||
import 'dart:math'; | |||
import 'package:flutter/cupertino.dart'; | |||
@@ -5,6 +6,7 @@ import 'package:flutter/material.dart'; | |||
import 'package:flutter_bloc/flutter_bloc.dart'; | |||
import 'package:flutter_swiper/flutter_swiper.dart'; | |||
import 'package:tab_indicator_styler/tab_indicator_styler.dart'; | |||
import 'package:zhiying_base_widget/widgets/primary_page_view/primary_page_view.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'package:cached_network_image/cached_network_image.dart'; | |||
import 'custom_quick_entry_sk.dart'; | |||
@@ -19,9 +21,9 @@ class CustomQuickEntry extends StatelessWidget { | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocProvider<CustomQuickEntryBloc>( | |||
create: (_) => | |||
CustomQuickEntryBloc(repository: CustomQuickEntryRepository()) | |||
..add(CustomQuickEntryInitEvent(model: model)), | |||
create: (_) { | |||
return CustomQuickEntryBloc(repository: CustomQuickEntryRepository())..add(CustomQuickEntryInitEvent(model: model)); | |||
}, | |||
child: _CustomQuickEntryContainer(model), | |||
); | |||
} | |||
@@ -30,13 +32,13 @@ class CustomQuickEntry extends StatelessWidget { | |||
class _CustomQuickEntryContainer extends StatefulWidget { | |||
final Map<String, dynamic> model; | |||
_CustomQuickEntryContainer(this.model); | |||
_CustomQuickEntryContainer(this.model, {Key key}) : super(key: key); | |||
@override | |||
__CustomQuickEntryContainerState createState() => __CustomQuickEntryContainerState(); | |||
} | |||
class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> { | |||
class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { | |||
/// Icon点击事件 | |||
void _itemIconClick(ListStyle model) { | |||
print("item type = ${model.skipIdentifier}"); | |||
@@ -49,12 +51,14 @@ class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> | |||
@override | |||
void initState() { | |||
_controller = SwiperController(); | |||
super.initState(); | |||
} | |||
@override | |||
void dispose() { | |||
_controller?.dispose(); | |||
super.dispose(); | |||
} | |||
@@ -63,14 +67,24 @@ class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> | |||
return BlocConsumer<CustomQuickEntryBloc, CustomQuickEntryState>( | |||
listener: (context, state) {}, | |||
buildWhen: (prev, current) { | |||
if (prev is CustomQuickEntryLoadedState) { | |||
return false; | |||
} | |||
return true; | |||
}, | |||
builder: (context, state) { | |||
print("构建"); | |||
if (state is CustomQuickEntryCachedState) { | |||
return _getMainWidget(state.model); | |||
} | |||
if (state is CustomQuickEntryLoadedState) { | |||
return _getMainWidget(state.model); | |||
if (state.model.isShowCategory == "1") { | |||
return CustomQuickCateEntry( | |||
model: state.model, | |||
); | |||
} else { | |||
return _getMainWidget(state.model); | |||
} | |||
} | |||
if (state is CustomQuickEntryErrorState) { | |||
return Container(); | |||
@@ -91,15 +105,15 @@ class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> | |||
int columSize = int.parse(model?.columSize ?? '5'); | |||
//计算实际显示行数 | |||
if(totalRowSize*columSize>totalDataSize){ | |||
for(int index=1;index<=totalRowSize;index++){ | |||
if(index*columSize>=totalDataSize){ | |||
totalRowSize=index; | |||
if (totalRowSize * columSize > totalDataSize) { | |||
for (int index = 1; index <= totalRowSize; index++) { | |||
if (index * columSize >= totalDataSize) { | |||
totalRowSize = index; | |||
break; | |||
} | |||
} | |||
} | |||
print("行数"+totalRowSize.toString()); | |||
print("行数" + totalRowSize.toString()); | |||
// 图标的高度 | |||
double iconHeight = 40.0; | |||
@@ -128,7 +142,7 @@ class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> | |||
// 总体高度 = 行数 * (子元素高度 + 边距高度) + 进度条的高度 | |||
double totalHeight = totalRowSize * (itemHeight + barMargin); | |||
if (totalPage > 1 && !EmptyUtil.isEmpty(model?.pagination) && model.pagination != 'type_null' /*model.pagination_open == '0'*/) { | |||
totalHeight = totalRowSize * (itemHeight + barMargin); | |||
totalHeight = totalRowSize * (itemHeight + barMargin)+8; | |||
} | |||
// 分类导航高度 | |||
@@ -150,10 +164,7 @@ class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> | |||
double marginLeftRight = model?.isLeftRightMargin == "1" ? double.tryParse(model?.leftRightMargin ?? "0") : 0; | |||
double marginTop = model?.isTopMargin == "1" ? double.tryParse(model?.topMargin ?? "0") : 0; | |||
return Container( | |||
margin: EdgeInsets.only(top: marginTop, | |||
left: marginLeftRight, | |||
right: marginLeftRight), | |||
margin: EdgeInsets.only(top: marginTop, left: marginLeftRight, right: marginLeftRight), | |||
decoration: BoxDecoration( | |||
color: HexColor.fromHex(model?.bgColor ?? ''), | |||
//color: Colors.green, | |||
@@ -162,10 +173,9 @@ class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> | |||
topLeft: Radius.circular(ParseUtil.stringParseDouble(model?.topLeftRadius)), | |||
bottomLeft: Radius.circular(ParseUtil.stringParseDouble(model?.bottomLeftRadius)), | |||
bottomRight: Radius.circular(ParseUtil.stringParseDouble(model?.bottomRightRadius)), | |||
) | |||
), | |||
)), | |||
child: Container( | |||
margin: EdgeInsets.only(top: !hasCategory ? 15 : 0, bottom: totalPage > 1 ? 15 : 0), | |||
margin: EdgeInsets.only(top: !hasCategory ? 15 : 0, bottom: totalPage > 1 ? 8 : 0), | |||
height: totalHeight, | |||
// 总体高度 | |||
width: double.infinity, | |||
@@ -173,12 +183,11 @@ class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> | |||
child: Column( | |||
crossAxisAlignment: CrossAxisAlignment.start, | |||
children: <Widget>[ | |||
// 左上角标题 | |||
Visibility( | |||
visible: !EmptyUtil.isEmpty(model?.moduleTitleName), | |||
child: Padding( | |||
padding: const EdgeInsets.only(left: 12.5, bottom: 10), | |||
padding: const EdgeInsets.only(left: 12.5, bottom: 10), | |||
child: Text(model?.moduleTitleName ?? '', style: TextStyle(color: HexColor.fromHex(model?.moduleTitleColor), fontWeight: FontWeight.bold, fontSize: 15))), | |||
), | |||
@@ -272,7 +281,6 @@ class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> | |||
// )), | |||
// ), | |||
// ) | |||
], | |||
), | |||
), | |||
@@ -311,16 +319,17 @@ class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> | |||
} | |||
/// 行的数据 | |||
Widget _getRowWidget({double titleHeight, | |||
double iconHeight, | |||
int totalPage, | |||
int currentPage, | |||
int columSize, | |||
int totalRowSize, | |||
int totalDataSize, | |||
CustomQuickEntryModel model, | |||
int currentRow, | |||
double itemHeight}) { | |||
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); | |||
@@ -348,19 +357,18 @@ class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> | |||
} | |||
/// 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, | |||
}) { | |||
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 : | |||
@@ -389,7 +397,6 @@ class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> | |||
// color: Colors.red, | |||
child: Column( | |||
children: <Widget>[ | |||
/// 图标 | |||
MyNetWorkImage(item.img), | |||
@@ -481,6 +488,10 @@ class __CustomQuickEntryContainerState extends State<_CustomQuickEntryContainer> | |||
); | |||
}); | |||
} | |||
@override | |||
// TODO: implement wantKeepAlive | |||
bool get wantKeepAlive => true; | |||
} | |||
/// | |||
@@ -495,14 +506,10 @@ class MyNetWorkImage extends StatelessWidget { | |||
@override | |||
Widget build(BuildContext context) { | |||
return RepaintBoundary( | |||
child: CachedNetworkImage( | |||
width: width, | |||
imageUrl: imgUrl | |||
), | |||
child: CachedNetworkImage(width: width, imageUrl: imgUrl), | |||
); | |||
} | |||
// Widget _getMainWidget(CustomQuickEntryModel model) { | |||
// // 数据总数 | |||
// int totalDataSize = model?.listStyle?.length ?? 0; | |||
@@ -580,3 +587,519 @@ class MyNetWorkImage extends StatelessWidget { | |||
// } | |||
} | |||
class CustomQuickCateEntry extends StatefulWidget { | |||
final CustomQuickEntryModel model; | |||
const CustomQuickCateEntry({Key key, this.model}) : super(key: key); | |||
@override | |||
_CustomQuickCateEntryState createState() => _CustomQuickCateEntryState(); | |||
} | |||
class _CustomQuickCateEntryState extends State<CustomQuickCateEntry> with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { | |||
TabController tabController; | |||
PrimaryPageController primaryPageController; | |||
bool isOnTap = false; | |||
Timer onTapTimer; | |||
SwiperController _controller; | |||
List<PageItem> pageList = List(); | |||
@override | |||
void initState() { | |||
// primaryPageController = PrimaryPageController(keepPage: true); | |||
if (widget?.model != null) { | |||
tabController = TabController(length: widget?.model?.typeList?.length ?? 0, vsync: this); | |||
tabController.addListener(() {}); | |||
} | |||
_controller = SwiperController(); | |||
// primaryPageController.addListener(() { | |||
// if (isOnTap) { | |||
// return; | |||
// } | |||
// if ((primaryPageController.page - tabController.index).abs() > 0.9) { | |||
// tabController.animateTo(primaryPageController.page.floor()); | |||
// } | |||
// }); | |||
super.initState(); | |||
} | |||
@override | |||
void dispose() { | |||
tabController?.dispose(); | |||
primaryPageController?.dispose(); | |||
for (var item in widget.model?.typeList) { | |||
item.primaryPageController?.dispose(); | |||
item.primaryPageController = null; | |||
} | |||
super.dispose(); | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
super.build(context); | |||
print("构建数据"); | |||
return _getMainHasCategoryWidget(widget?.model); | |||
} | |||
Widget _getMainHasCategoryWidget(CustomQuickEntryModel model) { | |||
bool hasCategory = false; | |||
if (!EmptyUtil.isEmpty(model?.typeList) && model.typeList.length >= 2) { | |||
hasCategory = true; | |||
} | |||
int maxTotal = 0; | |||
///分类 | |||
if (model.typeList != null) { | |||
for (var item in model?.typeList) { | |||
item.listStyle = List(); | |||
if (item.primaryPageController == null) { | |||
item.primaryPageController = PrimaryPageController(keepPage: true); | |||
} | |||
for (var listItem in model?.listStyle) { | |||
if (listItem?.typeListKey == item.key) { | |||
item.listStyle.add(listItem); | |||
} | |||
} | |||
if (item.listStyle.length > maxTotal) { | |||
maxTotal = item.listStyle.length; | |||
} | |||
} | |||
} | |||
pageList.clear(); | |||
// 数据总数 | |||
int totalDataSize = maxTotal; | |||
// 展示的列数 | |||
int columSize = int.parse(model?.columSize ?? '5'); | |||
// 展示的总行数 | |||
int totalRowSize = int.parse(model?.rowSize ?? '2'); | |||
//计算实际显示行数 | |||
if (totalRowSize * columSize > totalDataSize) { | |||
for (int index = 1; index <= totalRowSize; index++) { | |||
if (index * columSize >= totalDataSize) { | |||
totalRowSize = index; | |||
break; | |||
} | |||
} | |||
} | |||
//计算实际显示行数 | |||
print("行数" + totalRowSize.toString()); | |||
// 图标的高度 | |||
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 = 10.0; | |||
// 总页数 | |||
int totalPage = totalDataSize % (totalRowSize * columSize) == 0 ? totalDataSize ~/ (totalRowSize * columSize) : (totalDataSize ~/ (totalRowSize * columSize)) + 1; | |||
Logger.log('totalPage = ' + totalPage?.toString()); | |||
// 总体高度 = 行数 * (子元素高度 + 边距高度) + 进度条的高度 | |||
double totalHeight = totalRowSize * (itemHeight + barMargin); | |||
if (totalPage > 1 && !EmptyUtil.isEmpty(model?.pagination) && model.pagination != 'type_null' /*model.pagination_open == '0'*/) { | |||
totalHeight = totalRowSize * (itemHeight + barMargin)+8; | |||
} | |||
// 分类导航高度 | |||
double categoryHeight = 24; | |||
// 分类导航到多眼导航的边距 | |||
double categoryBottomMargin = 20.0; | |||
if (!EmptyUtil.isEmpty(model?.typeList) && model.typeList.length >= 2) { | |||
totalHeight = totalHeight + categoryHeight + categoryBottomMargin; | |||
hasCategory = true; | |||
} | |||
// 如果有左上角标题 | |||
double moduleTitleHeight = 22 + 10.0; | |||
if (!EmptyUtil.isEmpty(model?.moduleTitleName)) { | |||
totalHeight = totalHeight + moduleTitleHeight; | |||
} | |||
double marginLeftRight = model?.isLeftRightMargin == "1" ? double.tryParse(model?.leftRightMargin ?? "0") : 0; | |||
double marginTop = model?.isTopMargin == "1" ? double.tryParse(model?.topMargin ?? "0") : 0; | |||
int currentTypeIndex = 0; | |||
for (var item in model.typeList) { | |||
if ((item?.listStyle?.length??0) == 0) { | |||
pageList.add(PageItem(index: currentTypeIndex, listStyle: List())); | |||
currentTypeIndex++; | |||
continue; | |||
} | |||
if (item.listStyle.length > totalRowSize) { | |||
for (int index = 0; index < (item.listStyle.length / (columSize * totalRowSize)).ceil(); index++) { | |||
var list = List<ListStyle>(); | |||
var startIndex = index * columSize * totalRowSize; | |||
for (var i = 0; i < columSize * totalRowSize; i++) { | |||
if (item.listStyle.length > startIndex + i) { | |||
list.add(item.listStyle[startIndex+i]); | |||
} else { | |||
break; | |||
} | |||
} | |||
pageList.add(PageItem(index: currentTypeIndex, listStyle: list)); | |||
} | |||
}else{ | |||
pageList.add(PageItem(index: currentTypeIndex, listStyle: item?.listStyle)); | |||
} | |||
currentTypeIndex++; | |||
} | |||
return Container( | |||
margin: EdgeInsets.only(top: marginTop, left: marginLeftRight, right: marginLeftRight), | |||
decoration: BoxDecoration( | |||
color: HexColor.fromHex(model?.bgColor ?? ''), | |||
//color: Colors.green, | |||
borderRadius: BorderRadius.only( | |||
topRight: Radius.circular(ParseUtil.stringParseDouble(model?.topRightRadius)), | |||
topLeft: Radius.circular(ParseUtil.stringParseDouble(model?.topLeftRadius)), | |||
bottomLeft: Radius.circular(ParseUtil.stringParseDouble(model?.bottomLeftRadius)), | |||
bottomRight: Radius.circular(ParseUtil.stringParseDouble(model?.bottomRightRadius)), | |||
)), | |||
child: Container( | |||
height: totalHeight+(totalRowSize*5), | |||
margin: EdgeInsets.only(bottom: model?.pagination=="type_null"?0:8), | |||
child: Column( | |||
crossAxisAlignment: CrossAxisAlignment.start, | |||
children: <Widget>[ | |||
hasCategory | |||
? Container( | |||
height: categoryHeight, | |||
margin: EdgeInsets.only(top: 10,bottom: 10), | |||
child: TabBar( | |||
isScrollable: true, | |||
onTap: (value) { | |||
onTapTimer?.cancel(); | |||
isOnTap=true; | |||
onTapTimer = Timer(Duration(milliseconds: 300), () { | |||
isOnTap = false; | |||
}); | |||
int current = 0; | |||
for (var pageItem in pageList) { | |||
if (pageItem.index == value) { | |||
_controller.move(current); | |||
print("跳转到" + current.toString()); | |||
break; | |||
} | |||
current++; | |||
} | |||
}, | |||
labelColor: HexColor.fromHex("#FFFFFF"), | |||
unselectedLabelColor: HexColor.fromHex("#FF333333"), | |||
labelStyle: TextStyle(fontSize: 14), | |||
tabs: _buildTab(model), | |||
controller: tabController, | |||
indicatorSize: TabBarIndicatorSize.label, | |||
indicatorColor: Colors.transparent, | |||
indicator: BubbleTabIndicator(indicatorColor: HexColor.fromHex("#FF4242")), | |||
), | |||
) | |||
: Container(), | |||
Expanded( | |||
child: Swiper( | |||
controller: _controller, | |||
itemCount: pageList.length ?? 0, | |||
physics: BouncingScrollPhysics(), | |||
loop: false, | |||
onIndexChanged: (currentIndex) { | |||
if (isOnTap) { | |||
return; | |||
} | |||
tabController.animateTo(pageList[currentIndex].index); | |||
}, | |||
itemBuilder: (context, index) { | |||
var totalDataSize = pageList[index].listStyle.length ?? 0; | |||
return Container( | |||
height: double.infinity, | |||
width: double.infinity, | |||
padding: const EdgeInsets.symmetric(horizontal: 4), | |||
child: _getCategoryPageWidget( | |||
iconHeight: iconHeight, | |||
titleHeight: titleHeight, | |||
totalDataSize: totalDataSize, | |||
totalPage: pageList?.length??0, | |||
currentPage: index, | |||
totalRowSize: totalRowSize, | |||
columSize: columSize, | |||
model: model, | |||
itemHeight: itemHeight, | |||
), | |||
); | |||
}, | |||
pagination: _getSwiperPaginationContorl(model, model?.typeList?.length ?? 0), | |||
)), | |||
], | |||
), | |||
), | |||
); | |||
} | |||
_buildTab(CustomQuickEntryModel model) { | |||
List<Widget> listWidget = List(); | |||
for (var item in model?.typeList) { | |||
listWidget.add(Tab( | |||
text: item.name, | |||
)); | |||
} | |||
return listWidget; | |||
} | |||
/// 获取有分类页 | |||
_getCategoryPageWidget( | |||
{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: _buildHasCategoryRowWidget( | |||
titleHeight: titleHeight, | |||
iconHeight: iconHeight, | |||
totalPage: totalPage, | |||
currentPage: currentPage, | |||
columSize: columSize, | |||
totalRowSize: totalRowSize, | |||
totalDataSize: totalDataSize, | |||
model: model, | |||
currentRow: currentRow, | |||
itemHeight: itemHeight), | |||
); | |||
}).toList(), | |||
); | |||
} | |||
///有分类行构建 | |||
Widget _buildHasCategoryRowWidget( | |||
{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 _getHasCategoryColumWidget( | |||
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 _getHasCategoryColumWidget({ | |||
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 ; | |||
print("当前页"+currentPage.toString()+"当前点"+currentIndex.toString()); | |||
// print('current Index sss = $currentIndex'); | |||
if (currentIndex >= totalDataSize) { | |||
return Container( | |||
height: itemHeight, | |||
width: 60, | |||
); | |||
} | |||
ListStyle item = pageList[currentPage].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)), | |||
), | |||
), | |||
) | |||
], | |||
), | |||
), | |||
); | |||
} | |||
/// Icon点击事件 | |||
void _itemIconClick(ListStyle model) { | |||
print("item type = ${model.skipIdentifier}"); | |||
// Navigator.push(context, CupertinoPageRoute(builder: (_) => CommonPage(null))); | |||
RouterUtil.route(model, model.toJson(), context); | |||
} | |||
/// 进度条 | |||
SwiperPlugin _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(), | |||
), | |||
); | |||
}); | |||
} | |||
@override | |||
// TODO: implement wantKeepAlive | |||
bool get wantKeepAlive => true; | |||
} | |||
class PageItem { | |||
int index; | |||
PageItem({this.index, this.listStyle}); | |||
List<ListStyle> listStyle; | |||
} |
@@ -1,4 +1,5 @@ | |||
import 'package:zhiying_base_widget/dialog/global_dialog/intellect_search_goods_dialog/model/no_goods_dialog_style_model.dart'; | |||
import 'package:zhiying_base_widget/widgets/primary_page_view/primary_page_view.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class CustomQuickEntryModel { | |||
@@ -191,6 +192,9 @@ class ListStyle extends SkipModel{ | |||
class TypeList { | |||
String name; | |||
String key; | |||
List<ListStyle> listStyle; | |||
PrimaryPageController primaryPageController; | |||
int index; | |||
TypeList({this.name, this.key}); | |||
@@ -21,7 +21,7 @@ import 'package:zhiying_comm/util/turn_chain/turn_chain_util.dart'; | |||
class GoodsDetailsFooterWidget extends StatelessWidget { | |||
final Map<String, dynamic> model; | |||
final bool isFree; | |||
const GoodsDetailsFooterWidget(this.model,{this.isFree}); | |||
const GoodsDetailsFooterWidget(this.model,{this.isFree=false}); | |||
@override | |||
Widget build(BuildContext context) { | |||
@@ -34,6 +34,7 @@ class GoodsDetailsFooterWidget extends StatelessWidget { | |||
child: _GoodsDetailsFooterContainer( | |||
model, | |||
key: UniqueKey(), | |||
isFree: isFree, | |||
), | |||
), | |||
); | |||
@@ -144,7 +144,7 @@ class _UpgradeTipWidgetState extends State<UpgradeTipWidget> { | |||
return _isiOSReview ? Container() : Container( | |||
width: double.infinity, | |||
margin: EdgeInsets.only(top: ParseUtil.stringParseDouble(widget._model?.topMargin), left: ParseUtil.stringParseDouble(widget._model?.leftRightMargin), right: ParseUtil.stringParseDouble(widget._model?.leftRightMargin)), | |||
padding: const EdgeInsets.only(left: 12.5, right: 12.5, top: 6, bottom: 6), | |||
padding: const EdgeInsets.only(left: 10, right: 10, top: 0, bottom: 0), | |||
decoration: BoxDecoration( | |||
color: HexColor.fromHex(widget._model?.bgColor), | |||
// color: Colors.white, | |||
@@ -163,7 +163,7 @@ class _UpgradeTipWidgetState extends State<UpgradeTipWidget> { | |||
color: HexColor.fromHex(widget._model?.bulletinBgColor ?? '#FFEFDA'), | |||
borderRadius: BorderRadius.circular(30), | |||
), | |||
padding: const EdgeInsets.only(left: 10, right: 13, top: 10, bottom: 10), | |||
padding: const EdgeInsets.only(left: 10,right: 10,top: 9.5, bottom: 9.5), | |||
child: Row( | |||
mainAxisAlignment: MainAxisAlignment.spaceBetween, | |||
children: <Widget>[ | |||
@@ -185,19 +185,20 @@ class _UpgradeTipWidgetState extends State<UpgradeTipWidget> { | |||
CachedNetworkImage( | |||
imageUrl: model?.icon ?? '', | |||
width: 15, | |||
fit: BoxFit.fitWidth, | |||
), | |||
const SizedBox(width: 7.5), | |||
/// 文字 | |||
Text(model?.bulletinText ?? '下载APP升级运营商,享受更多收益', style: TextStyle(color: HexColor.fromHex(model?.bulletinTextColor ?? '#C09023'), fontSize: 11)) | |||
Text(model?.bulletinText ?? '下载APP升级运营商,享受更多收益', style: TextStyle(color: HexColor.fromHex(model?.bulletinTextColor ?? '#C09023'), height: 1,fontSize: 11)) | |||
], | |||
); | |||
} | |||
/// 右边的视图 | |||
Widget _getRightWidget(UpgradeTipModel model) { | |||
return Text(model?.goText ?? '前往下载', style: TextStyle(color: HexColor.fromHex(model?.bulletinTextColor ?? '#C09023'), fontSize: 11)); | |||
return Text(model?.goText ?? '前往下载', style: TextStyle(color: HexColor.fromHex(model?.bulletinTextColor ?? '#C09023'),height: 1, fontSize: 11)); | |||
// return Row( | |||
// children: <Widget>[ | |||
// Text(model?.go_text ??'前往下载', style: TextStyle(color: HexColor.fromHex('#C09023'), fontSize: 11)), | |||
@@ -102,38 +102,41 @@ class _MineHeaderContainerState extends State<MineHeaderContainer> { | |||
fontSize: 17, color: HexColor.fromHex(widget.staticModel.userNameColor)), | |||
), | |||
), | |||
Container( | |||
height: 18, | |||
margin: EdgeInsets.only(left: 6), | |||
padding: EdgeInsets.only(left: 6, right: 6), | |||
decoration: boxDecoration, | |||
// BoxDecoration( | |||
// image: DecorationImage( | |||
// fit: BoxFit.fitWidth, | |||
// image: NetworkImage( | |||
// widget.staticModel.lvBgImage, | |||
// | |||
// )), | |||
// // color: HexColor.fromHex(widget.staticModel.bgColor), | |||
// // borderRadius: BorderRadius.circular(9) | |||
// ), | |||
child: Row( | |||
children: <Widget>[ | |||
Container( | |||
width: 12, | |||
height: 12, | |||
margin: EdgeInsets.only(right: 2), | |||
child: CachedNetworkImage( | |||
imageUrl: profile?.userLvIcon ?? '', | |||
fit: BoxFit.contain, | |||
Visibility( | |||
visible: (widget?.staticModel?.status??"1")=="1"?true:false, | |||
child: Container( | |||
height: 18, | |||
margin: EdgeInsets.only(left: 6), | |||
padding: EdgeInsets.only(left: 6, right: 6), | |||
decoration: boxDecoration, | |||
// BoxDecoration( | |||
// image: DecorationImage( | |||
// fit: BoxFit.fitWidth, | |||
// image: NetworkImage( | |||
// widget.staticModel.lvBgImage, | |||
// | |||
// )), | |||
// // color: HexColor.fromHex(widget.staticModel.bgColor), | |||
// // borderRadius: BorderRadius.circular(9) | |||
// ), | |||
child: Row( | |||
children: <Widget>[ | |||
Container( | |||
width: 12, | |||
height: 12, | |||
margin: EdgeInsets.only(right: 2), | |||
child: CachedNetworkImage( | |||
imageUrl: profile?.userLvIcon ?? '', | |||
fit: BoxFit.contain, | |||
), | |||
), | |||
), | |||
Text( | |||
profile?.userLvName ?? '', | |||
style: TextStyle( | |||
fontSize: 10, color: HexColor.fromHex(widget.staticModel.lvNameColor)), | |||
) | |||
], | |||
Text( | |||
profile?.userLvName ?? '', | |||
style: TextStyle( | |||
fontSize: 10, color: HexColor.fromHex(widget.staticModel.lvNameColor)), | |||
) | |||
], | |||
), | |||
), | |||
), | |||
], | |||
@@ -30,6 +30,7 @@ class MineHeaderModel { | |||
int requiredLogin; | |||
int requiredTaobaoAuth; | |||
String skipIdentifier; | |||
String status; | |||
MineHeaderModel( | |||
{this.name, | |||
@@ -96,6 +97,7 @@ class MineHeaderModel { | |||
requiredLogin = json['required_login']; | |||
requiredTaobaoAuth = json['required_taobao_auth']; | |||
skipIdentifier = json['skip_identifier']; | |||
status=json['status']; | |||
} | |||
Map<String, dynamic> toJson() { | |||
@@ -0,0 +1,980 @@ | |||
import 'dart:async'; | |||
import 'dart:math' as math; | |||
import 'package:flutter/material.dart'; | |||
import 'package:flutter/physics.dart'; | |||
import 'package:flutter/rendering.dart'; | |||
import 'package:flutter/gestures.dart' show Drag, DragStartBehavior; | |||
import 'package:flutter/foundation.dart' show precisionErrorTolerance; | |||
///支持嵌套滑动的pageView | |||
///需要开启Primary=true | |||
class PrimaryPageController extends ScrollController { | |||
PrimaryPageController({ | |||
this.initialPage = 0, | |||
this.keepPage = true, | |||
this.viewportFraction = 1.0, | |||
this.coordinator, | |||
}) : assert(initialPage != null), | |||
assert(keepPage != null), | |||
assert(viewportFraction != null), | |||
assert(viewportFraction > 0.0); | |||
int initialPage; | |||
final bool keepPage; | |||
final double viewportFraction; | |||
PrimaryPageCoordinator coordinator; | |||
List<PrimaryPageController> childPageController = []; | |||
double get page { | |||
assert( | |||
positions.isNotEmpty, | |||
'PrimaryPageController.page cannot be accessed before a NestedPrimaryPageView is built with it.', | |||
); | |||
assert( | |||
positions.length == 1, | |||
'The page property cannot be read when multiple PageViews are attached to ' | |||
'the same PrimaryPageController.', | |||
); | |||
final PrimaryPagePosition position = this.position; | |||
return position.page; | |||
} | |||
Future<void> animateToPage( | |||
int page, { | |||
@required Duration duration, | |||
@required Curve curve, | |||
}) { | |||
final PrimaryPagePosition position = this.position; | |||
return position.animateTo( | |||
position.getPixelsFromPage(page.toDouble()), | |||
duration: duration, | |||
curve: curve, | |||
); | |||
} | |||
void jumpToPage(int page) { | |||
final PrimaryPagePosition position = this.position; | |||
position.jumpTo(position.getPixelsFromPage(page.toDouble())); | |||
} | |||
Future<void> nextPage({@required Duration duration, @required Curve curve}) { | |||
return animateToPage(page.round() + 1, duration: duration, curve: curve); | |||
} | |||
Future<void> previousPage( | |||
{@required Duration duration, @required Curve curve}) { | |||
return animateToPage(page.round() - 1, duration: duration, curve: curve); | |||
} | |||
@override | |||
ScrollPosition createScrollPosition(ScrollPhysics physics, | |||
ScrollContext context, ScrollPosition oldPosition) { | |||
PrimaryPagePosition result = PrimaryPagePosition( | |||
physics: physics, | |||
context: context, | |||
initialPage: initialPage, | |||
keepPage: keepPage, | |||
viewportFraction: viewportFraction, | |||
oldPosition: oldPosition, | |||
)..coordinator = coordinator; | |||
return result; | |||
} | |||
@override | |||
void attach(ScrollPosition position) { | |||
super.attach(position); | |||
final PrimaryPagePosition pagePosition = position; | |||
pagePosition.viewportFraction = viewportFraction; | |||
if (position is PrimaryPagePosition) { | |||
position.coordinator = coordinator; | |||
} | |||
} | |||
@override | |||
void detach(ScrollPosition position) { | |||
super.detach(position); | |||
} | |||
} | |||
class PrimaryPagePosition extends ScrollPosition | |||
implements PageMetrics, ScrollActivityDelegate { | |||
PrimaryPagePosition({ | |||
ScrollPhysics physics, | |||
ScrollContext context, | |||
this.initialPage = 0, | |||
bool keepPage = true, | |||
double viewportFraction = 1.0, | |||
double initialPixels = 0.0, | |||
ScrollPosition oldPosition, | |||
}) : assert(initialPage != null), | |||
assert(keepPage != null), | |||
assert(viewportFraction != null), | |||
assert(viewportFraction > 0.0), | |||
_viewportFraction = viewportFraction, | |||
_pageToUseOnStartup = initialPage.toDouble(), | |||
super( | |||
physics: physics, | |||
context: context, | |||
keepScrollOffset: keepPage, | |||
oldPosition: oldPosition, | |||
) { | |||
if (activity == null) goIdle(); | |||
assert(activity != null); | |||
} | |||
final int initialPage; | |||
double _pageToUseOnStartup; | |||
@override | |||
double get viewportFraction => _viewportFraction; | |||
double _viewportFraction; | |||
PrimaryPageCoordinator coordinator; | |||
set viewportFraction(double value) { | |||
if (_viewportFraction == value) return; | |||
final double oldPage = page; | |||
_viewportFraction = value; | |||
if (oldPage != null) forcePixels(getPixelsFromPage(oldPage)); | |||
} | |||
double get _initialPageOffset => | |||
math.max(0, viewportDimension * (viewportFraction - 1) / 2); | |||
ScrollActivity get scrollActivity => activity; | |||
double getPageFromPixels(double pixels, double viewportDimension) { | |||
final double actual = math.max(0.0, pixels - _initialPageOffset) / | |||
math.max(1.0, viewportDimension * viewportFraction); | |||
final double round = actual.roundToDouble(); | |||
if ((actual - round).abs() < precisionErrorTolerance) { | |||
return round; | |||
} | |||
return actual; | |||
} | |||
double getPixelsFromPage(double page) { | |||
return page * viewportDimension * viewportFraction + _initialPageOffset; | |||
} | |||
@override | |||
double get page { | |||
assert( | |||
pixels == null || (minScrollExtent != null && maxScrollExtent != null), | |||
'Page value is only available after content dimensions are established.', | |||
); | |||
return pixels == null | |||
? null | |||
: getPageFromPixels( | |||
pixels.clamp(minScrollExtent, maxScrollExtent), viewportDimension); | |||
} | |||
@override | |||
void saveScrollOffset() { | |||
PageStorage.of(context.storageContext)?.writeState( | |||
context.storageContext, getPageFromPixels(pixels, viewportDimension)); | |||
} | |||
@override | |||
void restoreScrollOffset() { | |||
if (pixels == null) { | |||
final double value = PageStorage.of(context.storageContext) | |||
?.readState(context.storageContext); | |||
if (value != null) _pageToUseOnStartup = value; | |||
} | |||
} | |||
@override | |||
bool applyViewportDimension(double viewportDimension) { | |||
final double oldViewportDimensions = this.viewportDimension; | |||
final bool result = super.applyViewportDimension(viewportDimension); | |||
final double oldPixels = pixels; | |||
final double page = (oldPixels == null || oldViewportDimensions == 0.0) | |||
? _pageToUseOnStartup | |||
: getPageFromPixels(oldPixels, this.viewportDimension); | |||
final double newPixels = getPixelsFromPage(page); | |||
if (newPixels != oldPixels) { | |||
correctPixels(newPixels); | |||
return false; | |||
} | |||
return result; | |||
} | |||
@override | |||
bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) { | |||
final double newMinScrollExtent = minScrollExtent + _initialPageOffset; | |||
return super.applyContentDimensions( | |||
newMinScrollExtent, | |||
math.max(newMinScrollExtent, maxScrollExtent - _initialPageOffset), | |||
); | |||
} | |||
@override | |||
PageMetrics copyWith({ | |||
double minScrollExtent, | |||
double maxScrollExtent, | |||
double pixels, | |||
double viewportDimension, | |||
AxisDirection axisDirection, | |||
double viewportFraction, | |||
}) { | |||
return PageMetrics( | |||
minScrollExtent: minScrollExtent ?? this.minScrollExtent, | |||
maxScrollExtent: maxScrollExtent ?? this.maxScrollExtent, | |||
pixels: pixels ?? this.pixels, | |||
viewportDimension: viewportDimension ?? this.viewportDimension, | |||
axisDirection: axisDirection ?? this.axisDirection, | |||
viewportFraction: viewportFraction ?? this.viewportFraction, | |||
); | |||
} | |||
/// Velocity from a previous activity temporarily held by [hold] to potentially | |||
/// transfer to a next activity. | |||
double _heldPreviousVelocity = 0.0; | |||
@override | |||
AxisDirection get axisDirection => context.axisDirection; | |||
@override | |||
double setPixels(double newPixels) { | |||
print("current position:" + newPixels.toString()); | |||
return super.setPixels(newPixels); | |||
} | |||
@override | |||
void absorb(ScrollPosition other) { | |||
super.absorb(other); | |||
if (other is! ScrollPositionWithSingleContext) { | |||
goIdle(); | |||
return; | |||
} | |||
activity.updateDelegate(this); | |||
final PrimaryPagePosition typedOther = other; | |||
_userScrollDirection = typedOther._userScrollDirection; | |||
assert(_currentDrag == null); | |||
if (typedOther._currentDrag != null) { | |||
_currentDrag = typedOther._currentDrag; | |||
_currentDrag.updateDelegate(this); | |||
typedOther._currentDrag = null; | |||
} | |||
} | |||
@override | |||
void applyNewDimensions() { | |||
super.applyNewDimensions(); | |||
context.setCanDrag(physics.shouldAcceptUserOffset(this)); | |||
} | |||
@override | |||
void beginActivity(ScrollActivity newActivity) { | |||
_heldPreviousVelocity = 0.0; | |||
if (newActivity == null) return; | |||
super.beginActivity(newActivity); | |||
_currentDrag?.dispose(); | |||
_currentDrag = null; | |||
if (!activity.isScrolling) updateUserScrollDirection(ScrollDirection.idle); | |||
} | |||
@override | |||
void applyUserOffset(double delta) { | |||
updateUserScrollDirection( | |||
delta > 0.0 ? ScrollDirection.forward : ScrollDirection.reverse); | |||
setPixels(pixels - physics.applyPhysicsToUserOffset(this, delta)); | |||
} | |||
@override | |||
void goIdle() { | |||
beginActivity(IdleScrollActivity(this)); | |||
} | |||
/// Start a physics-driven simulation that settles the [pixels] position, | |||
/// starting at a particular velocity. | |||
/// | |||
/// This method defers to [ScrollPhysics.createBallisticSimulation], which | |||
/// typically provides a bounce simulation when the current position is out of | |||
/// bounds and a friction simulation when the position is in bounds but has a | |||
/// non-zero velocity. | |||
/// | |||
/// The velocity should be in logical pixels per second. | |||
@override | |||
void goBallistic(double velocity) { | |||
assert(pixels != null); | |||
final Simulation simulation = | |||
physics.createBallisticSimulation(this, velocity); | |||
if (simulation != null) { | |||
beginActivity(BallisticScrollActivity(this, simulation, context.vsync)); | |||
} else { | |||
goIdle(); | |||
} | |||
} | |||
@override | |||
ScrollDirection get userScrollDirection => _userScrollDirection; | |||
ScrollDirection _userScrollDirection = ScrollDirection.idle; | |||
/// Set [userScrollDirection] to the given value. | |||
/// | |||
/// If this changes the value, then a [UserScrollNotification] is dispatched. | |||
@protected | |||
@visibleForTesting | |||
void updateUserScrollDirection(ScrollDirection value) { | |||
assert(value != null); | |||
if (userScrollDirection == value) return; | |||
_userScrollDirection = value; | |||
didUpdateScrollDirection(value); | |||
} | |||
@override | |||
Future<void> animateTo( | |||
double to, { | |||
@required Duration duration, | |||
@required Curve curve, | |||
}) { | |||
if (nearEqual(to, pixels, physics.tolerance.distance)) { | |||
// Skip the animation, go straight to the position as we are already close. | |||
jumpTo(to); | |||
return Future<void>.value(); | |||
} | |||
final DrivenScrollActivity activity = DrivenScrollActivity( | |||
this, | |||
from: pixels, | |||
to: to, | |||
duration: duration, | |||
curve: curve, | |||
vsync: context.vsync, | |||
); | |||
beginActivity(activity); | |||
return activity.done; | |||
} | |||
@override | |||
void jumpTo(double value) { | |||
goIdle(); | |||
if (pixels != value) { | |||
final double oldPixels = pixels; | |||
forcePixels(value); | |||
notifyListeners(); | |||
didStartScroll(); | |||
didUpdateScrollPositionBy(pixels - oldPixels); | |||
didEndScroll(); | |||
} | |||
goBallistic(0.0); | |||
} | |||
@Deprecated( | |||
'This will lead to bugs.') // ignore: flutter_deprecation_syntax, https://github.com/flutter/flutter/issues/44609 | |||
@override | |||
void jumpToWithoutSettling(double value) { | |||
goIdle(); | |||
if (pixels != value) { | |||
final double oldPixels = pixels; | |||
forcePixels(value); | |||
notifyListeners(); | |||
didStartScroll(); | |||
didUpdateScrollPositionBy(pixels - oldPixels); | |||
didEndScroll(); | |||
} | |||
} | |||
@override | |||
ScrollHoldController hold(VoidCallback holdCancelCallback) { | |||
if (coordinator != null && coordinator.isOuterControllerEnable()) { | |||
return coordinator.hold(holdCancelCallback); | |||
} else { | |||
final double previousVelocity = activity.velocity; | |||
final HoldScrollActivity holdActivity = HoldScrollActivity( | |||
delegate: this, | |||
onHoldCanceled: holdCancelCallback, | |||
); | |||
beginActivity(holdActivity); | |||
_heldPreviousVelocity = previousVelocity; | |||
return holdActivity; | |||
} | |||
} | |||
ScrollDragController _currentDrag; | |||
@override | |||
Drag drag(DragStartDetails details, VoidCallback dragCancelCallback) { | |||
if (coordinator != null && coordinator.isOuterControllerEnable()) { | |||
return coordinator.drag(details, dragCancelCallback); | |||
} else { | |||
final ScrollDragController drag = ScrollDragController( | |||
delegate: this.coordinator ?? this, | |||
details: details, | |||
onDragCanceled: dragCancelCallback, | |||
carriedVelocity: physics.carriedMomentum(_heldPreviousVelocity), | |||
motionStartDistanceThreshold: physics.dragStartDistanceMotionThreshold, | |||
); | |||
beginActivity(DragScrollActivity(this, drag)); | |||
assert(_currentDrag == null); | |||
_currentDrag = drag; | |||
return drag; | |||
} | |||
} | |||
@override | |||
void dispose() { | |||
_currentDrag?.dispose(); | |||
_currentDrag = null; | |||
super.dispose(); | |||
} | |||
@override | |||
void debugFillDescription(List<String> description) { | |||
super.debugFillDescription(description); | |||
description.add('${context.runtimeType}'); | |||
description.add('$physics'); | |||
description.add('$activity'); | |||
description.add('$userScrollDirection'); | |||
} | |||
// Returns the amount of delta that was not used. | |||
// | |||
// Positive delta means going down (exposing stuff above), negative delta | |||
// going up (exposing stuff below). | |||
double applyClampedDragUpdate(double delta) { | |||
assert(delta != 0.0); | |||
// If we are going towards the maxScrollExtent (negative scroll offset), | |||
// then the furthest we can be in the minScrollExtent direction is negative | |||
// infinity. For example, if we are already overscrolled, then scrolling to | |||
// reduce the overscroll should not disallow the overscroll. | |||
// | |||
// If we are going towards the minScrollExtent (positive scroll offset), | |||
// then the furthest we can be in the minScrollExtent direction is wherever | |||
// we are now, if we are already overscrolled (in which case pixels is less | |||
// than the minScrollExtent), or the minScrollExtent if we are not. | |||
// | |||
// In other words, we cannot, via applyClampedDragUpdate, _enter_ an | |||
// overscroll situation. | |||
// | |||
// An overscroll situation might be nonetheless entered via several means. | |||
// One is if the physics allow it, via applyFullDragUpdate (see below). An | |||
// overscroll situation can also be forced, e.g. if the scroll position is | |||
// artificially set using the scroll controller. | |||
final double min = | |||
delta < 0.0 ? -double.infinity : math.min(minScrollExtent, pixels); | |||
// The logic for max is equivalent but on the other side. | |||
final double max = | |||
delta > 0.0 ? double.infinity : math.max(maxScrollExtent, pixels); | |||
final double oldPixels = pixels; | |||
final double newPixels = (pixels - delta).clamp(min, max); | |||
final double clampedDelta = newPixels - pixels; | |||
if (clampedDelta == 0.0) return delta; | |||
final double overscroll = physics.applyBoundaryConditions(this, newPixels); | |||
final double actualNewPixels = newPixels - overscroll; | |||
final double offset = actualNewPixels - oldPixels; | |||
if (offset != 0.0) { | |||
forcePixels(actualNewPixels); | |||
didUpdateScrollPositionBy(offset); | |||
} | |||
return delta + offset; | |||
} | |||
// Returns the overscroll. | |||
double applyFullDragUpdate(double delta) { | |||
assert(delta != 0.0); | |||
final double oldPixels = pixels; | |||
// Apply friction: | |||
final double newPixels = | |||
pixels - physics.applyPhysicsToUserOffset(this, delta); | |||
if (oldPixels == newPixels) | |||
return 0.0; // delta must have been so small we dropped it during floating point addition | |||
// Check for overscroll: | |||
final double overscroll = physics.applyBoundaryConditions(this, newPixels); | |||
final double actualNewPixels = newPixels - overscroll; | |||
if (actualNewPixels != oldPixels) { | |||
forcePixels(actualNewPixels); | |||
didUpdateScrollPositionBy(actualNewPixels - oldPixels); | |||
} | |||
if (overscroll != 0.0) { | |||
didOverscrollBy(overscroll); | |||
return overscroll; | |||
} | |||
return 0.0; | |||
} | |||
} | |||
class PrimaryPageCoordinator | |||
implements ScrollActivityDelegate, ScrollHoldController { | |||
PrimaryPageController _outerController; | |||
PrimaryPageController _selfController; | |||
ScrollDragController _currentDrag; | |||
bool isOperateBody = false; | |||
PrimaryPageCoordinator(PrimaryPageController selfController, | |||
PrimaryPageController parentController) { | |||
_selfController = selfController; | |||
_selfController.coordinator = this; | |||
_outerController = parentController; | |||
_outerController.coordinator = this; | |||
if (!_outerController.childPageController.contains(_selfController)) { | |||
_outerController.childPageController.add(_selfController); | |||
} | |||
} | |||
PrimaryPageController getOuterController() { | |||
return _outerController; | |||
} | |||
bool isOuterControllerEnable() { | |||
return _outerController != null && _outerController.hasClients; | |||
} | |||
PrimaryPageController getInnerController() { | |||
return _selfController; | |||
} | |||
bool isInnerControllerEnable() { | |||
return _selfController != null && _selfController.hasClients; | |||
} | |||
@override | |||
void applyUserOffset(double delta) { | |||
updateUserScrollDirection( | |||
delta > 0.0 ? ScrollDirection.forward : ScrollDirection.reverse); | |||
// PrimaryPagePosition innerPosition = | |||
// (getInnerController().position as PrimaryPagePosition); | |||
PrimaryPagePosition outPosition = isOuterControllerEnable() | |||
? (getOuterController().position as PrimaryPagePosition) | |||
: null; | |||
// /// 如果不在头尾,且内部page可滑动 | |||
// if ((outPosition.pixels > outPosition?.minScrollExtent && | |||
// outPosition.pixels < outPosition?.maxScrollExtent) && | |||
// (delta < 0 | |||
// ? innerPosition.pixels < innerPosition.maxScrollExtent | |||
// : innerPosition.pixels > innerPosition.minScrollExtent)) { | |||
// isOperateBody = true; | |||
// innerPosition.applyUserOffset(delta); | |||
// return; | |||
// } | |||
/// 如果快速滑动,没结束掉动画那种,那么在这里判断下是否需要传给子Page | |||
if ((outPosition.pixels >= outPosition?.minScrollExtent && | |||
outPosition.pixels <= outPosition?.maxScrollExtent)) { | |||
// if (outPosition.pixels % outPosition.viewportDimension != 0) { | |||
// outPosition.applyUserOffset(delta); | |||
// return; | |||
// } | |||
if (getOuterController().childPageController.length > | |||
outPosition.page.round()) { | |||
PrimaryPageController currentChildPageController = | |||
getOuterController().childPageController[outPosition.page.round()]; | |||
PrimaryPagePosition currentInnerPosition = | |||
currentChildPageController.position; | |||
if (delta < 0 | |||
? currentInnerPosition.pixels < currentInnerPosition.maxScrollExtent | |||
: currentInnerPosition.pixels > | |||
currentInnerPosition.minScrollExtent) { | |||
isOperateBody = true; | |||
outPosition.goBallistic(0.0); | |||
currentInnerPosition.applyUserOffset(delta); | |||
return; | |||
} | |||
} | |||
} | |||
/// 都不符合,那才通过外部滑动 | |||
isOperateBody = false; | |||
outPosition.applyUserOffset(delta); | |||
} | |||
@override | |||
AxisDirection get axisDirection => _outerController.position.axisDirection; | |||
@override | |||
void cancel() { | |||
goBallistic(0.0); | |||
} | |||
@override | |||
void goBallistic(double velocity) { | |||
PrimaryPagePosition outPosition = isOuterControllerEnable() | |||
? (getOuterController().position as PrimaryPagePosition) | |||
: null; | |||
if (getOuterController().childPageController.length > | |||
outPosition.page.round()) { | |||
PrimaryPageController currentChildPageController = | |||
getOuterController().childPageController[outPosition.page.round()]; | |||
PrimaryPagePosition currentInnerPosition = | |||
currentChildPageController.position; | |||
// if (isOperateBody) { | |||
if (!isOperateBody && | |||
(outPosition != null) && | |||
(outPosition.pixels > outPosition.minScrollExtent && | |||
outPosition.pixels < outPosition.maxScrollExtent)) { | |||
outPosition.goBallistic(velocity); | |||
currentInnerPosition.goBallistic(0.0); | |||
_currentDrag?.dispose(); | |||
_currentDrag = null; | |||
return; | |||
} | |||
if (isOperateBody && velocity > 0) { | |||
if (currentInnerPosition.pixels < currentInnerPosition.maxScrollExtent && | |||
currentInnerPosition.pixels > currentInnerPosition.minScrollExtent) { | |||
outPosition.goBallistic(0.0); | |||
currentInnerPosition.goBallistic(velocity); | |||
} else { | |||
outPosition?.goBallistic(velocity); | |||
currentInnerPosition.goBallistic(0.0); | |||
} | |||
} else { | |||
if (currentInnerPosition.pixels < currentInnerPosition.maxScrollExtent) { | |||
outPosition.goBallistic(0.0); | |||
currentInnerPosition.goBallistic(velocity); | |||
} else { | |||
outPosition?.goBallistic(velocity); | |||
currentInnerPosition.goBallistic(0.0); | |||
} | |||
} | |||
} | |||
_currentDrag?.dispose(); | |||
_currentDrag = null; | |||
} | |||
@override | |||
void goIdle() { | |||
beginActivity(IdleScrollActivity(this), IdleScrollActivity(this)); | |||
} | |||
@override | |||
double setPixels(double pixels) { | |||
return 0.0; | |||
} | |||
ScrollHoldController hold(VoidCallback holdCancelCallback) { | |||
beginActivity( | |||
HoldScrollActivity(delegate: this, onHoldCanceled: holdCancelCallback), | |||
HoldScrollActivity(delegate: this, onHoldCanceled: holdCancelCallback)); | |||
return this; | |||
} | |||
Drag drag(DragStartDetails details, VoidCallback dragCancelCallback) { | |||
final ScrollDragController drag = ScrollDragController( | |||
delegate: this, | |||
details: details, | |||
onDragCanceled: dragCancelCallback, | |||
); | |||
beginActivity( | |||
DragScrollActivity(this, drag), DragScrollActivity(this, drag)); | |||
assert(_currentDrag == null); | |||
_currentDrag = drag; | |||
return drag; | |||
} | |||
void beginActivity( | |||
ScrollActivity newOuterActivity, ScrollActivity newInnerActivity) { | |||
getInnerController().position.beginActivity(newInnerActivity); | |||
if (isOuterControllerEnable()) { | |||
getOuterController().position.beginActivity(newOuterActivity); | |||
} | |||
_currentDrag?.dispose(); | |||
_currentDrag = null; | |||
if ((newOuterActivity == null || !newOuterActivity.isScrolling) && | |||
(newInnerActivity == null || !newInnerActivity.isScrolling)) { | |||
updateUserScrollDirection(ScrollDirection.idle); | |||
} | |||
} | |||
ScrollDirection get userScrollDirection => _userScrollDirection; | |||
ScrollDirection _userScrollDirection = ScrollDirection.idle; | |||
void updateUserScrollDirection(ScrollDirection value) { | |||
assert(value != null); | |||
if (userScrollDirection == value) return; | |||
_userScrollDirection = value; | |||
getOuterController().position.didUpdateScrollDirection(value); | |||
if (isOuterControllerEnable()) { | |||
getInnerController().position.didUpdateScrollDirection(value); | |||
} | |||
} | |||
} | |||
const PageScrollPhysics _kPagePhysics = PageScrollPhysics(); | |||
class PrimaryPageView extends StatefulWidget { | |||
/// Creates a scrollable list that works page by page from an explicit [List] | |||
/// of widgets. | |||
/// | |||
/// This constructor is appropriate for page views with a small number of | |||
/// children because constructing the [List] requires doing work for every | |||
/// child that could possibly be displayed in the page view, instead of just | |||
/// those children that are actually visible. | |||
PrimaryPageView({ | |||
Key key, | |||
this.scrollDirection = Axis.horizontal, | |||
this.reverse = false, | |||
PrimaryPageController controller, | |||
this.physics, | |||
this.pageSnapping = true, | |||
this.onPageChanged, | |||
this.primary = false, | |||
List<Widget> children = const <Widget>[], | |||
this.dragStartBehavior = DragStartBehavior.start, | |||
}) : childrenDelegate = SliverChildListDelegate(children), | |||
super(key: key) { | |||
if (controller == null) { | |||
this.controller = PrimaryPageController(); | |||
} else { | |||
this.controller = controller; | |||
} | |||
} | |||
/// Creates a scrollable list that works page by page using widgets that are | |||
/// created on demand. | |||
///delta | |||
/// This constructor is appropriate for page views with a large (or infinite) | |||
/// number of children because the builder is called only for those children | |||
/// that are actually visible. | |||
/// | |||
/// Providing a non-null [itemCount] lets the [PrimaryPageView] compute the maximum | |||
/// scroll extent. | |||
/// | |||
/// [itemBuilder] will be called only with indices greater than or equal to | |||
/// zero and less than [itemCount]. | |||
/// | |||
/// [PrimaryPageView.builder] by default does not support child reordering. If | |||
/// you are planning to change child order at a later time, consider using | |||
/// [PrimaryPageView] or [PrimaryPageView.custom]. | |||
PrimaryPageView.builder({ | |||
Key key, | |||
this.scrollDirection = Axis.horizontal, | |||
this.reverse = false, | |||
PrimaryPageController controller, | |||
this.physics, | |||
this.pageSnapping = true, | |||
this.onPageChanged, | |||
@required IndexedWidgetBuilder itemBuilder, | |||
int itemCount, | |||
this.primary = false, | |||
this.dragStartBehavior = DragStartBehavior.start, | |||
}) : childrenDelegate = | |||
SliverChildBuilderDelegate(itemBuilder, childCount: itemCount), | |||
super(key: key) { | |||
if (controller == null) { | |||
this.controller = PrimaryPageController(); | |||
} else { | |||
this.controller = controller; | |||
} | |||
} | |||
PrimaryPageView.custom({ | |||
Key key, | |||
this.scrollDirection = Axis.horizontal, | |||
this.reverse = false, | |||
PrimaryPageController controller, | |||
this.physics, | |||
this.pageSnapping = true, | |||
this.onPageChanged, | |||
@required this.childrenDelegate, | |||
this.primary = false, | |||
this.dragStartBehavior = DragStartBehavior.start, | |||
}) : assert(childrenDelegate != null), | |||
super(key: key) { | |||
if (controller == null) { | |||
this.controller = PrimaryPageController(); | |||
} else { | |||
this.controller = controller; | |||
} | |||
} | |||
/// The axis along which the page view scrolls. | |||
/// | |||
/// Defaults to [Axis.horizontal]. | |||
final Axis scrollDirection; | |||
/// Whether the page view scrolls in the reading direction. | |||
/// | |||
/// For example, if the reading direction is left-to-right and | |||
/// [scrollDirection] is [Axis.horizontal], then the page view scrolls from | |||
/// left to right when [reverse] is false and from right to left when | |||
/// [reverse] is true. | |||
/// | |||
/// Similarly, if [scrollDirection] is [Axis.vertical], then the page view | |||
/// scrolls from top to bottom when [reverse] is false and from bottom to top | |||
/// when [reverse] is true. | |||
/// | |||
/// Defaults to false. | |||
final bool reverse; | |||
/// An object that can be used to control the position to which this page | |||
/// view is scrolled. | |||
PrimaryPageController controller; | |||
/// How the page view should respond to user input. | |||
/// | |||
/// For example, determines how the page view continues to animate after the | |||
/// user stops dragging the page view. | |||
/// | |||
/// The physics are modified to snap to page boundaries using | |||
/// [PageScrollPhysics] prior to being used. | |||
/// | |||
/// Defaults to matching platform conventions. | |||
final ScrollPhysics physics; | |||
/// Set to false to disable page snapping, useful for custom scroll behavior. | |||
final bool pageSnapping; | |||
/// Called whenever the page in the center of the viewport changes. | |||
final ValueChanged<int> onPageChanged; | |||
/// A delegate that provides the children for the [PrimaryPageView]. | |||
/// | |||
/// The [PrimaryPageView.custom] constructor lets you specify this delegate | |||
/// explicitly. The [PrimaryPageView] and [PrimaryPageView.builder] constructors create a | |||
/// [childrenDelegate] that wraps the given [List] and [IndexedWidgetBuilder], | |||
/// respectively. | |||
final SliverChildDelegate childrenDelegate; | |||
/// {@macro flutter.widgets.scrollable.dragStartBehavior} | |||
final DragStartBehavior dragStartBehavior; | |||
final bool primary; | |||
@override | |||
_PageViewState createState() => _PageViewState(); | |||
} | |||
class _PageViewState extends State<PrimaryPageView> { | |||
int _lastReportedPage = 0; | |||
@override | |||
void initState() { | |||
super.initState(); | |||
_lastReportedPage = widget.controller.initialPage; | |||
} | |||
@override | |||
void didChangeDependencies() { | |||
super.didChangeDependencies(); | |||
} | |||
AxisDirection _getDirection(BuildContext context) { | |||
switch (widget.scrollDirection) { | |||
case Axis.horizontal: | |||
assert(debugCheckHasDirectionality(context)); | |||
final TextDirection textDirection = Directionality.of(context); | |||
final AxisDirection axisDirection = | |||
textDirectionToAxisDirection(textDirection); | |||
return widget.reverse | |||
? flipAxisDirection(axisDirection) | |||
: axisDirection; | |||
case Axis.vertical: | |||
return widget.reverse ? AxisDirection.up : AxisDirection.down; | |||
} | |||
return null; | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
final AxisDirection axisDirection = _getDirection(context); | |||
final ScrollPhysics physics = widget.pageSnapping | |||
? _kPagePhysics.applyTo(widget.physics) | |||
: widget.physics; | |||
final ScrollController scrollController = widget.primary | |||
? PrimaryScrollController.of(context) | |||
: widget.controller; | |||
if (widget.primary && scrollController is PrimaryPageController) { | |||
scrollController.initialPage = _lastReportedPage; | |||
PrimaryPageCoordinator(widget.controller, scrollController); | |||
} | |||
return NotificationListener<ScrollNotification>( | |||
onNotification: (ScrollNotification notification) { | |||
if (notification.depth == 0 && | |||
widget.onPageChanged != null && | |||
notification is ScrollUpdateNotification) { | |||
final PageMetrics metrics = notification.metrics; | |||
final int currentPage = metrics.page.round(); | |||
if (currentPage != _lastReportedPage) { | |||
_lastReportedPage = currentPage; | |||
widget.onPageChanged(currentPage); | |||
} | |||
} | |||
return false; | |||
}, | |||
child: Scrollable( | |||
/// magic code,魔法代码勿动……这个key很重要 | |||
/// 如果PrimaryPageView下面还有多个同级PrimaryPageView, | |||
/// 那么竟然会导致父PrimaryPageView识别为子PrimaryPageView | |||
/// 进而在didUpdateWidget中解绑父PrimaryPageView中controller绑定的position | |||
/// 并将其赋予给子PrimaryPageView的controller | |||
/// 这样就导致父PrimaryPageView就这么神奇的丢失了自己的position…… | |||
/// 进而无法触发任何父PrimaryPageView的滑动事件 | |||
/// 不知道是我的问题还是flutter的问题 | |||
/// 不过既然知道原因了 | |||
/// 用key打个补丁,加上个身份证就好了……有空研究下这个神奇的问题 | |||
key: Key(widget.controller.toString()), | |||
dragStartBehavior: widget.dragStartBehavior, | |||
axisDirection: axisDirection, | |||
controller: widget.controller, | |||
physics: physics, | |||
viewportBuilder: (BuildContext context, ViewportOffset position) { | |||
return Viewport( | |||
cacheExtent: 0.0, | |||
axisDirection: axisDirection, | |||
offset: position, | |||
slivers: <Widget>[ | |||
PrimaryScrollController( | |||
controller: widget.controller, | |||
child: SliverFillViewport( | |||
viewportFraction: widget.controller.viewportFraction, | |||
delegate: widget.childrenDelegate, | |||
)), | |||
], | |||
); | |||
}, | |||
), | |||
); | |||
} | |||
@override | |||
void debugFillProperties(DiagnosticPropertiesBuilder description) { | |||
super.debugFillProperties(description); | |||
description | |||
.add(EnumProperty<Axis>('scrollDirection', widget.scrollDirection)); | |||
description.add( | |||
FlagProperty('reverse', value: widget.reverse, ifTrue: 'reversed')); | |||
description.add(DiagnosticsProperty<PrimaryPageController>( | |||
'controller', widget.controller, | |||
showName: false)); | |||
description.add(DiagnosticsProperty<ScrollPhysics>( | |||
'physics', widget.physics, | |||
showName: false)); | |||
description.add(FlagProperty('pageSnapping', | |||
value: widget.pageSnapping, ifFalse: 'snapping disabled')); | |||
} | |||
} |
@@ -34,8 +34,14 @@ class _ShareAlertState extends State<ShareAlert> { | |||
@override | |||
void initState() { | |||
NetUtil.request('/api/v1/mod/${widget.skipIdentifier}', method: NetMethod.GET, onCache: (data) { | |||
_parseData(data); | |||
// try{ | |||
// _parseData(data); | |||
// }catch(e){ | |||
// print(e); | |||
// } | |||
}, onSuccess: (data) { | |||
print(data); | |||
_parseData(data); | |||
}, onError: (err) {}); | |||
@@ -247,9 +253,20 @@ class _ShareAlertContentState extends State<_ShareAlertContent> { | |||
); | |||
} | |||
} else { | |||
var type=SSDKContentTypes.auto; | |||
if(widget?.model?.image?.first!=null&&widget.model?.url!=null){ | |||
type=SSDKContentTypes.webpage; | |||
}else if(widget?.model?.image?.first!=null){ | |||
type=SSDKContentTypes.image; | |||
}else if(widget?.model?.title!=null||widget.model?.content!=null){ | |||
type=SSDKContentTypes.text; | |||
} | |||
params = SSDKMap() | |||
..setGeneral( | |||
widget.model?.title ?? '', | |||
widget.model?.title ?? '123', | |||
widget.model?.content ?? '', | |||
Platform.isIOS ? widget.model.image : null, | |||
Platform.isAndroid ? widget?.model?.image?.first : null, | |||
@@ -259,7 +276,7 @@ class _ShareAlertContentState extends State<_ShareAlertContent> { | |||
null, | |||
null, | |||
null, | |||
SSDKContentTypes.auto, | |||
type, | |||
); | |||
} | |||
SharesdkPlugin.share(plateform, params, (SSDKResponseState state, Map userdata, Map contentEntity, SSDKError error) { | |||