misc.dart 11 KB

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