misc.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. import 'dart:math';
  2. import 'package:cached_network_image/cached_network_image.dart';
  3. import 'package:dartin/dartin.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter_easyrefresh/easy_refresh.dart';
  6. import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
  7. import 'package:provider/provider.dart';
  8. import 'package:sport/bean/post_user.dart';
  9. import 'package:sport/bean/user.dart';
  10. import 'package:sport/constant/ui.dart';
  11. import 'package:sport/pages/my/achievement_detail_page.dart';
  12. import 'package:sport/provider/user_model.dart';
  13. import 'package:sport/router/navigator_util.dart';
  14. import 'package:sport/services/api/inject_api.dart';
  15. import 'package:sport/services/api/login_api.dart';
  16. import 'package:sport/widgets/dialog/popupmenu.dart' as menu;
  17. import 'package:sport/widgets/image.dart';
  18. import 'package:sport/widgets/refresh_header.dart' as header;
  19. import 'package:sport/widgets/refresh_footer.dart';
  20. import 'package:sport/widgets/space.dart';
  21. Widget buildSocialUserWidget(BuildContext context, PostUser user, int createTime, int avatarWidth) {
  22. return user == null
  23. ? Container()
  24. : GestureDetector(
  25. onTap: () => NavigatorUtil.goSocialUserDetail(context, user),
  26. child: Row(
  27. children: <Widget>[
  28. Space(
  29. width: 12,
  30. ),
  31. CircleAvatar(backgroundImage: userAvatarProvider(user?.avatar), radius: avatarWidth / 2),
  32. Space(
  33. width: 8,
  34. ),
  35. Text(
  36. user?.name,
  37. style: Theme.of(context).textTheme.subtitle1.copyWith(fontWeight: FontWeight.w600),
  38. ),
  39. ],
  40. ));
  41. }
  42. Widget buildLabelWidget(BuildContext context, String title) {
  43. return Container(
  44. padding: EdgeInsets.fromLTRB(ui_padding, 10.0, ui_padding, 10.0),
  45. child: Text(
  46. title,
  47. style: Theme.of(context).textTheme.headline1.copyWith(fontSize: 16.0,),
  48. ));
  49. }
  50. Widget gameTag(BuildContext context, String name) {
  51. return Container(
  52. decoration: BoxDecoration(
  53. borderRadius: BorderRadius.all(Radius.circular(50)),
  54. border: Border.all(
  55. color: Theme.of(context).accentColor,
  56. width: 1,
  57. ),
  58. ),
  59. padding: EdgeInsets.fromLTRB(8, 0, 8, 1),
  60. child: Text(
  61. name,
  62. style: Theme.of(context).textTheme.bodyText1.copyWith(color: Theme.of(context).accentColor),
  63. strutStyle: fixedLine,
  64. ),
  65. );
  66. }
  67. Widget achievementGroupWidget(List<Achievement> achievements) {
  68. return achievements == null || achievements.isEmpty
  69. ? Center(child: Container(child: Padding(padding: const EdgeInsets.all(24.0), child: Text("还未获得成就"))))
  70. : StaggeredGridView.countBuilder(
  71. padding: EdgeInsets.zero,
  72. shrinkWrap: true,
  73. physics: NeverScrollableScrollPhysics(),
  74. crossAxisCount: 4,
  75. itemCount: min(4, achievements.length),
  76. itemBuilder: (BuildContext context, int index) => achievementWidget(context, achievements[index]),
  77. crossAxisSpacing: 12.0,
  78. staggeredTileBuilder: (int index) => StaggeredTile.fit(1),
  79. );
  80. }
  81. Widget achievementWidget(BuildContext context, Achievement item, {double w = 70, bool replace = false, bool isRadius = false, bool jump = true,showCount=false}) => InkWell(
  82. onTap: () async {
  83. // 不能看别人的成就...
  84. if(item.userId != null && Provider.of<UserModel>(context, listen: false).user.id != item.userId)
  85. return;
  86. // if (jump == true) NavigatorUtil.goAchievementDetails(context, id: item.id, userId: item.userId ?? 0, replace: replace);
  87. if(jump == true) {
  88. List<Achievement> relateAchievements = (await inject<LoginApi>().getAchieveDetailInfo(item.id)).data.relateAchievements;
  89. showSharePopup(context,relateAchievements,item.id);
  90. }
  91. },
  92. child: Stack(
  93. children: <Widget>[
  94. Column(
  95. children: <Widget>[
  96. isRadius
  97. ? CircleAvatar(backgroundImage: CachedNetworkImageProvider(item.logo ?? ""), radius: w / 2)
  98. : item?.createdAt == ""
  99. ? ColorFiltered(
  100. colorFilter: ColorFilter.mode(Color(0xffF1F1F1), BlendMode.color),
  101. child: CachedNetworkImage(width: w, height: w, imageUrl: item.logo ?? ""),
  102. )
  103. : Container(
  104. width: w,
  105. height: w,
  106. child: CachedNetworkImage(imageUrl: item.logo ?? ""),
  107. ),
  108. Space(
  109. height: 5,
  110. ),
  111. Text(
  112. item.seriesName != null ? item.seriesName : item.name,
  113. // style: Theme.of(context).textTheme.subtitle1.copyWith(fontSize: w < 80 ? 12 : 14),
  114. style:Theme.of(context).textTheme.subtitle1.copyWith(fontSize: 12)
  115. )
  116. ],
  117. ),
  118. showCount ? Positioned(
  119. top: 0,
  120. right: 10.0,
  121. child: Container(
  122. alignment: Alignment.center,
  123. width: 32.0,height: 21.0,
  124. decoration: BoxDecoration(
  125. image: DecorationImage(
  126. image: AssetImage("lib/assets/img/bg_achievement_number.png"),
  127. fit: BoxFit.cover,
  128. )
  129. ),
  130. child: Text("${item.seriesCount}枚",style:TextStyle(fontSize: 12.0,color:Colors.white),strutStyle: StrutStyle(forceStrutHeight: true, height: 0.8),),
  131. )
  132. ):Container()
  133. ],
  134. ));
  135. Widget sportBeEquivalentTo(BuildContext context, int consume, {bool highlight = false}) => Padding(
  136. padding: EdgeInsets.all(10.0),
  137. child: Text(
  138. "消耗了 ${(consume / 50).round()} 块小饼干",
  139. style: Theme.of(context).textTheme.subtitle1,
  140. ),
  141. );
  142. // Row(
  143. // mainAxisAlignment: MainAxisAlignment.spaceAround,
  144. // children: <Widget>[
  145. // Column(
  146. // children: <Widget>[
  147. // Image.asset("lib/assets/img/gamedetail_image_walk.png"),
  148. // Space(
  149. // height: 8,
  150. // ),
  151. // RichText(
  152. // text: TextSpan(children: <InlineSpan>[
  153. // TextSpan(text: '步行', style: Theme.of(context).textTheme.bodyText1),
  154. // TextSpan(
  155. // text: '${(consume / 3 * 90).floor()}',
  156. // style: highlight
  157. // ? Theme.of(context).textTheme.bodyText1.copyWith(color: Theme.of(context).accentColor)
  158. // : Theme.of(context).textTheme.bodyText1),
  159. // TextSpan(text: '步', style: Theme.of(context).textTheme.bodyText1),
  160. // ]),
  161. // )
  162. // ],
  163. // ),
  164. // Column(
  165. // children: <Widget>[
  166. // Image.asset("lib/assets/img/gamedetail_image_run.png"),
  167. // Space(
  168. // height: 8,
  169. // ),
  170. // RichText(
  171. // text: TextSpan(children: <InlineSpan>[
  172. // TextSpan(text: '跑步', style: Theme.of(context).textTheme.bodyText1),
  173. // TextSpan(
  174. // text: '${(consume / 60 / 1.036).toStringAsFixed(1)}',
  175. // style: highlight
  176. // ? Theme.of(context).textTheme.bodyText1.copyWith(color: Theme.of(context).accentColor)
  177. // : Theme.of(context).textTheme.bodyText1),
  178. // TextSpan(text: '公里', style: Theme.of(context).textTheme.bodyText1),
  179. // ]),
  180. // )
  181. // ],
  182. // ),
  183. // Column(
  184. // children: <Widget>[
  185. // Image.asset("lib/assets/img/gamedetail_image_riding.png"),
  186. // Space(
  187. // height: 8,
  188. // ),
  189. // RichText(
  190. // text: TextSpan(children: <InlineSpan>[
  191. // TextSpan(text: '单车', style: Theme.of(context).textTheme.bodyText1),
  192. // TextSpan(
  193. // text: '${(consume / 60 / 0.6142).toStringAsFixed(1)}',
  194. // style: highlight
  195. // ? Theme.of(context).textTheme.bodyText1.copyWith(color: Theme.of(context).accentColor)
  196. // : Theme.of(context).textTheme.bodyText1),
  197. // TextSpan(text: '公里', style: Theme.of(context).textTheme.bodyText1),
  198. // ]),
  199. // )
  200. // ],
  201. // ),
  202. // ],
  203. // );
  204. const REFRESH_INFO_COLOR = Color(0xff999999);
  205. header.ClassicalHeader buildClassicalHeader(
  206. {double extent = 80.0,
  207. double triggerDistance = 90.0,
  208. Color infoColor = REFRESH_INFO_COLOR,
  209. Color textColor = REFRESH_INFO_COLOR,
  210. Color bgColor = Colors.transparent,
  211. }) {
  212. return header.ClassicalHeader(
  213. extent: extent,
  214. triggerDistance: triggerDistance,
  215. showInfo: false,
  216. refreshText: '下拉刷新',
  217. refreshFailedText: '刷新失败',
  218. refreshedText: '刷新完成',
  219. refreshingText: '正在刷新...',
  220. refreshReadyText: '释放刷新',
  221. infoColor: infoColor,
  222. bgColor: bgColor,
  223. textColor: infoColor,
  224. );
  225. }
  226. Footer buildClassicalFooter() {
  227. return CustomClassicalFooter(
  228. showInfo: false,
  229. loadedText: '加载完成',
  230. loadReadyText: '释放加载',
  231. loadFailedText: '加载失败',
  232. loadText: '拉动加载',
  233. loadingText: '正在加载...',
  234. noMoreText: "没有更多了~",
  235. infoColor: Color(0xff999999));
  236. }
  237. PopupMenuEntry menuDivider() => menu.PopupMenuItem(
  238. height: 1,
  239. child: Divider(
  240. height: 1,
  241. ),
  242. );
  243. PopupMenuEntry menuItem(String value, String icon, String text) => menu.PopupMenuItem(
  244. value: value,
  245. child: Row(
  246. mainAxisSize: MainAxisSize.min,
  247. children: <Widget>[
  248. Image.asset(
  249. "lib/assets/img/$icon",
  250. width: 24,
  251. ),
  252. SizedBox(
  253. width: 4,
  254. ),
  255. Text(
  256. text,
  257. )
  258. ],
  259. ),
  260. );
  261. PopupMenuEntry menuItemCenter(dynamic value, String text) => menu.PopupMenuItem(
  262. value: value,
  263. child: Center(
  264. child: Text(
  265. text,
  266. )),
  267. );
  268. PopupMenuEntry menuItemSelected(dynamic value, String text, bool select) => menu.PopupMenuItem(
  269. value: value,
  270. child: Row(
  271. mainAxisSize: MainAxisSize.min,
  272. children: <Widget>[
  273. Container(
  274. alignment: AlignmentDirectional.centerStart,
  275. constraints: BoxConstraints(minWidth: 60),
  276. padding: const EdgeInsets.symmetric(horizontal: 8.0),
  277. child: select
  278. ? Text(
  279. text,
  280. style: TextStyle(color: Color(0xffFFC400)),
  281. )
  282. : Text(
  283. text,
  284. ),
  285. ),
  286. if (select)
  287. Image.asset(
  288. "lib/assets/img/pop_icon_selected.png",
  289. width: 24,
  290. ),
  291. ],
  292. ));
  293. List<PopupMenuEntry> divideMenus(Iterable<PopupMenuEntry> tiles) {
  294. assert(tiles != null);
  295. final Iterator<PopupMenuEntry> iterator = tiles.iterator;
  296. final PopupMenuEntry divider = menuDivider();
  297. final List<PopupMenuEntry> list = [];
  298. while (iterator.moveNext()) {
  299. list.add(iterator.current);
  300. list.add(divider);
  301. }
  302. if (list.length > 1) list.removeAt(list.length - 1);
  303. return list;
  304. }
  305. const StrutStyle fixedLine = StrutStyle(forceStrutHeight: true);