misc.dart 11 KB

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