menu_bar.dart 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. // import 'dart:async';
  2. // import 'dart:convert';
  3. // import 'dart:io';
  4. //
  5. // import 'package:flutter/cupertino.dart';
  6. // import 'package:flutter/material.dart';
  7. // import 'package:flutter/services.dart';
  8. // import 'package:images_picker/images_picker.dart';
  9. // import 'package:provider/provider.dart';
  10. // import 'package:sport/bean/message.dart';
  11. // import 'package:sport/provider/message_model.dart';
  12. // import 'package:sport/services/api/inject_api.dart';
  13. // import 'package:sport/utils/callback.dart';
  14. // import 'package:sport/utils/toast.dart';
  15. // import 'package:sport/widgets/button_primary.dart';
  16. // import 'package:sport/widgets/decoration.dart';
  17. // import 'package:sport/widgets/space.dart';
  18. //
  19. // import '../application.dart';
  20. // import 'dialog/request_dialog.dart';
  21. //
  22. // class MenuBar extends StatefulWidget {
  23. // final String? inputField;
  24. // final Function? closeMenuCallBack;
  25. // final Widget messageList;
  26. // final Function? scrollToBottom;
  27. // final MenuIdentity menuIdentity;
  28. // final Function? sendCallBack;
  29. // final GlobalKey? globalkey;
  30. // final IntCallback? keyboardCallBack;
  31. //
  32. // MenuBar(
  33. // this.messageList, {
  34. // required this.menuIdentity,
  35. // this.inputField,
  36. // this.closeMenuCallBack,
  37. // this.scrollToBottom,
  38. // this.sendCallBack,
  39. // this.globalkey,
  40. // this.keyboardCallBack,
  41. // });
  42. //
  43. // @override
  44. // State<StatefulWidget> createState() {
  45. // return _MenuBarState();
  46. // }
  47. // }
  48. //
  49. // class _MenuBarState extends State<MenuBar> with WidgetsBindingObserver, InjectApi {
  50. // GlobalKey _myKey = new GlobalKey(); // 用来定位Message位置
  51. //
  52. // String _textFieldValue = ""; // TextField的文本
  53. //
  54. // // TextField的 controller
  55. // TextEditingController? _controller;
  56. //
  57. // FocusNode _focusNode = new FocusNode(); // TextField 的 focus
  58. //
  59. // ValueNotifier<bool> _postable = ValueNotifier(false);
  60. //
  61. // double keyBoardHeight = 270.0; // 初始化下面menu的高度 后续会动态调整后 优化
  62. //
  63. // bool isFirst = true; // flag 优化menu高度的操作
  64. //
  65. // String? emojiJson; // 读取的JSON
  66. //
  67. // int isShowMenuBottomIndex = 0; // 用数字去优化if else 的判断
  68. //
  69. // double? height;
  70. //
  71. // Timer? _timer;
  72. //
  73. // bool showInput = true;
  74. //
  75. // @override
  76. // void didUpdateWidget(MenuBar oldWidget) {
  77. // super.didUpdateWidget(oldWidget);
  78. // widget.scrollToBottom?.call();
  79. // }
  80. //
  81. // void initState() {
  82. // super.initState();
  83. //
  84. // initEmoji();
  85. //
  86. // _controller = TextEditingController()
  87. // ..addListener(() {
  88. // _postable.value = _controller?.value.text.isNotEmpty == true;
  89. // });
  90. // _focusNode.addListener(() {
  91. // widget.keyboardCallBack?.call(isShowMenuBottomIndex);
  92. // });
  93. // }
  94. //
  95. // // 直接进来就请求了 不要 搞这些骚的....
  96. // void initEmoji() async {
  97. // String json = await DefaultAssetBundle.of(context).loadString("lib/assets/json/emoji_list.json");
  98. // setState(() {
  99. // emojiJson = json;
  100. // });
  101. // }
  102. //
  103. // //页面销毁
  104. // @override
  105. // void dispose() {
  106. // super.dispose();
  107. // //释放
  108. // _focusNode.dispose();
  109. // _timer?.cancel();
  110. // _postable.dispose();
  111. // _controller?.dispose();
  112. // }
  113. //
  114. // // 这里 插库 + 渲染更新...
  115. // Future add(MessageInstance? message) async {
  116. // if (message == null) return;
  117. //
  118. // GetIt.I<MessageModel>().add(context, [message]);
  119. // }
  120. //
  121. // _postFeedBackpostFeedBack(String content, {int typeId = 0}) async {
  122. // // typeId == null ? typeId = 0 : typeId = typeId;
  123. // // await api.postFeedback(typeId, content, extra: await Application.getDeviceInfo());
  124. // }
  125. //
  126. // _uploadImage(List<File> files) async {
  127. // if (widget.menuIdentity.menuScene == "chat") {
  128. // await request(context, () async {
  129. // for (var file in files) {
  130. // String path = file.path;
  131. // var data = (await api.postChatUpload(file)).data;
  132. // Image image = Image.file(File.fromUri(Uri.parse(file.path)));
  133. // image.image.resolve(ImageConfiguration()).addListener(ImageStreamListener((ImageInfo image, bool synchronousCall) {
  134. // // print("$data----------------------${image.image.width} - ${image.image.height}------");
  135. // api
  136. // .postChatSend(widget.menuIdentity.userId, "image", '{ "url":"${data['url']}", "w":${image.image.width}, "h":${image.image.height} }')
  137. // .then((value) => add(value.data));
  138. // file.delete();
  139. // }));
  140. // }
  141. // });
  142. // }
  143. // }
  144. //
  145. // Widget extMenuItem(
  146. // String text,
  147. // String url,
  148. // int index,
  149. // ) {
  150. // String error;
  151. // void getPicture() async {
  152. // try {
  153. // var resultList = await ImagesPicker.pick(quality: 0.8, maxSize: 1024, count: 9);
  154. //
  155. // if (resultList != null) {
  156. // List<File> files = [];
  157. // for (var i = 0; i < resultList.length; i++) {
  158. // Media asset = resultList[i];
  159. // File file = File(asset.path);
  160. // files.add(file);
  161. // }
  162. // _uploadImage(files);
  163. // }
  164. // } on Exception catch (e) {
  165. // error = e.toString();
  166. // }
  167. // }
  168. //
  169. // void getPhoto() async {
  170. // try {
  171. // // 拍完直接发...
  172. // final pickedFile = await ImagesPicker.openCamera(
  173. // pickType: PickType.image,
  174. // );
  175. //
  176. // if (pickedFile == null || pickedFile.isEmpty) return;
  177. // _uploadImage([File(pickedFile[0].path)]);
  178. // } on Exception catch (e) {
  179. // error = e.toString();
  180. // }
  181. // }
  182. //
  183. // List<Function> menuOperation = [getPicture, getPhoto];
  184. //
  185. // return InkWell(
  186. // child: Column(
  187. // crossAxisAlignment: CrossAxisAlignment.center,
  188. // children: <Widget>[
  189. // Container(
  190. // padding: EdgeInsets.fromLTRB(14.0, 16.0, 14.0, 14.0),
  191. // child: Image.asset("lib/assets/img/$url.png"),
  192. // decoration: BoxDecoration(
  193. // borderRadius: BorderRadius.circular(10.0),
  194. // color: Color(0xfff1f1f1),
  195. // ),
  196. // ),
  197. // SizedBox(
  198. // height: 5.0,
  199. // ),
  200. // Text("$text")
  201. // ],
  202. // ),
  203. // onTap: () {
  204. // menuOperation[index]();
  205. // },
  206. // );
  207. // }
  208. //
  209. // Widget _emoJiList() {
  210. // if (emojiJson != null && emojiJson?.length != 0) {
  211. // List<dynamic> data = json.decode(emojiJson!);
  212. // return Container(
  213. // height: keyBoardHeight,
  214. // padding: EdgeInsets.only(left: 5, top: 5, right: 5, bottom: 5),
  215. // decoration: BoxDecoration(color: Colors.white, border: Border(top: BorderSide(width: 1.0, color: Color(0xFFDCDCDC)))),
  216. // child: GridView.custom(
  217. // padding: EdgeInsets.all(3),
  218. // shrinkWrap: true,
  219. // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  220. // crossAxisCount: 6,
  221. // mainAxisSpacing: 0.5,
  222. // crossAxisSpacing: 6.0,
  223. // ),
  224. // childrenDelegate: SliverChildBuilderDelegate(
  225. // (context, index) {
  226. // return GestureDetector(
  227. // onTap: () {
  228. // String intPutString = (_controller?.text ?? "") + String.fromCharCode(data[index]["unicode"]);
  229. // var content = intPutString;
  230. // _controller?.value = TextEditingValue(
  231. // // 设置内容
  232. // text: content,
  233. // // 保持光标在最后
  234. // selection: TextSelection.fromPosition(TextPosition(affinity: TextAffinity.downstream, offset: content.length)));
  235. // // 主要是 onchange 没有办法 加上 表情 ...
  236. // setState(() {});
  237. // },
  238. // child: Center(
  239. // child: Text(
  240. // String.fromCharCode(data[index]["unicode"]),
  241. // style: TextStyle(fontSize: 33),
  242. // ),
  243. // ),
  244. // );
  245. // },
  246. // childCount: data.length,
  247. // ),
  248. // ),
  249. // );
  250. // }
  251. // return Container();
  252. // }
  253. //
  254. // Widget menuBottom() {
  255. // // 这里的软键盘动画还是有点问题....
  256. // List<Widget> list = [
  257. // Container(),
  258. // SizedBox(
  259. // height: MediaQuery.of(context).viewInsets.bottom,
  260. // ),
  261. // Container(
  262. // height: keyBoardHeight,
  263. // padding: EdgeInsets.only(left: 24.0, right: 24.0),
  264. // decoration: BoxDecoration(color: Colors.white, border: Border(top: BorderSide(width: 1.0, color: Color(0xFFDCDCDC)))),
  265. // child: GridView(
  266. // shrinkWrap: true,
  267. // padding: EdgeInsets.only(top: 24.0),
  268. // physics: NeverScrollableScrollPhysics(),
  269. // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  270. // crossAxisCount: 4, //横轴三个子widget
  271. // ),
  272. // children: <Widget>[
  273. // extMenuItem("图片", "bbs_icon_picture", 0),
  274. // extMenuItem("拍照", "bbs_icon_photo", 1),
  275. // ])),
  276. // _emoJiList(),
  277. // ];
  278. // return list[isShowMenuBottomIndex];
  279. // }
  280. //
  281. // Widget build(BuildContext context) {
  282. // // 优化 输入键盘高度 跟 菜单栏高度的操作
  283. // if (isFirst && MediaQuery.of(context).viewInsets.bottom != 0.0) {
  284. // setState(() {
  285. // keyBoardHeight = MediaQuery.of(context).viewInsets.bottom;
  286. // isFirst = false;
  287. // });
  288. // }
  289. //
  290. // return Column(
  291. // children: <Widget>[
  292. // Expanded(
  293. // child: GestureDetector(
  294. // behavior: HitTestBehavior.translucent,
  295. // onTap: () {
  296. // setState(() {
  297. // isShowMenuBottomIndex = 0;
  298. // });
  299. // _focusNode.unfocus();
  300. // },
  301. // child: Column(
  302. // children: <Widget>[
  303. // Flexible(
  304. // child: widget.messageList),
  305. // ],
  306. // ),
  307. // ),
  308. // ),
  309. // if (showInput)
  310. // // Column(
  311. // // children: <Widget>[
  312. // //// Container(
  313. // //// color: Colors.white,
  314. // //// padding: EdgeInsets.only(top: 10.0,left: 10.0,right: 10.0,bottom: 10.0),
  315. // //// child:
  316. // //// ),
  317. // // ,
  318. // // ],
  319. // // ),
  320. // Container(
  321. // padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
  322. // decoration: shadowTop(),
  323. // child: Row(
  324. // children: <Widget>[
  325. // GestureDetector(
  326. // onTap: () async {
  327. // if (_focusNode.hasFocus) {
  328. // await SystemChannels.textInput.invokeMethod('TextInput.hide');
  329. // widget.keyboardCallBack?.call(isShowMenuBottomIndex);
  330. // }
  331. // setState(() {
  332. // isShowMenuBottomIndex = 2;
  333. // });
  334. // },
  335. // child: Padding(
  336. // // padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
  337. // child: Image.asset("lib/assets/img/bbs_icon_addmore.png"),
  338. // padding: EdgeInsets.only(right: 12.0),
  339. // ),
  340. // ),
  341. // GestureDetector(
  342. // onTap: () {
  343. // FocusScope.of(context).requestFocus(_focusNode);
  344. // if (_focusNode.hasFocus) {
  345. // SystemChannels.textInput.invokeMethod('TextInput.hide');
  346. // widget.keyboardCallBack?.call(isShowMenuBottomIndex);
  347. // }
  348. // setState(() {
  349. // isShowMenuBottomIndex = 3;
  350. // });
  351. // },
  352. // child: Padding(
  353. // // padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
  354. // child: Image.asset("lib/assets/img/bbs_icon_expression.png"),
  355. // padding: EdgeInsets.only(right: 12.0),
  356. // ),
  357. // ),
  358. // Expanded(
  359. // child: CupertinoTextField(
  360. // cursorColor: const Color(0xffFFC400),
  361. // controller: _controller,
  362. // focusNode: _focusNode,
  363. // keyboardType: TextInputType.multiline,
  364. // style: TextStyle(
  365. // fontSize: 16.0,
  366. // ),
  367. // strutStyle: StrutStyle(forceStrutHeight: true, height: 1.4),
  368. // minLines: 1,
  369. // maxLines: 3,
  370. // // maxLength: 200,
  371. // onChanged: (value) {
  372. // // setState(() {
  373. // // // _textFieldValue = value;
  374. // // });
  375. // },
  376. // onTap: () {
  377. // setState(() {
  378. // isShowMenuBottomIndex = 1;
  379. // });
  380. // widget.scrollToBottom?.call();
  381. // },
  382. // decoration: BoxDecoration(shape: BoxShape.rectangle, borderRadius: BorderRadius.all(Radius.circular(10)), color: Color(0xfff1f1f1)),
  383. // ),
  384. // ),
  385. // Space(
  386. // width: 5.0,
  387. // ),
  388. // ValueListenableBuilder(
  389. // valueListenable: _postable,
  390. // builder: (_, able, __) => PrimaryButton(
  391. // width: 75,
  392. // height: 35.0,
  393. // content: "发送",
  394. // callback: () async {
  395. // if (_controller?.text.isEmpty == true) {
  396. // // ToastUtil.show("请输入正确的内容");
  397. // return;
  398. // }
  399. // var content = _controller?.text;
  400. // _controller?.clear();
  401. //
  402. // if (widget.menuIdentity.menuScene == "chat") {
  403. // MessageInstance? message = (await api.postChatSend(widget.menuIdentity.userId, "text", '{"text":"$content"}')).data;
  404. // if (message != null) await add(message); // await 是等待的标志 我等待完 在做后面的init 的事?
  405. // }
  406. // // if (widget.menuIdentity.menuScene == "feedback") {
  407. // // await _postFeedBackpostFeedBack(_textFieldValue);
  408. // // }
  409. //
  410. // // 这里可能传不了 callback 所有需要的值都在menu bar里面 只能通过传 不同的 场景 进行 不同的操作
  411. // },
  412. // shadow: able == true,
  413. // // buttonColor: able == false ? Color(0xffd2d2d2) : null,
  414. // buttonColor: able == false ? Color(0xffFFC400).withOpacity(0.3) : null,
  415. // )),
  416. // ],
  417. // )),
  418. // // 底部的骚操作
  419. // menuBottom(),
  420. // ],
  421. // );
  422. // }
  423. // }
  424. //
  425. // // // 只是用来封装 scrollToBottom
  426. // class GetMenuController {
  427. // ScrollController scrollMenuController = new ScrollController();
  428. //
  429. // // 暂时不用 这个 scrollToBottom ...
  430. // void scrollToBottom(BuildContext context, GlobalKey key, bool first) {
  431. // scrollMenuController.addListener(() {
  432. // print("[offSet:]${scrollMenuController.offset}----------------------");
  433. // });
  434. //
  435. // // 页面一屏的高度
  436. // double pageHeight = MediaQuery.of(context).size.height;
  437. //
  438. // // 头部的高度...
  439. // double appBarAndHeight = MediaQuery.of(context).padding.top + 56;
  440. //
  441. // // 尾部的高度...
  442. // double bottomHeight = 160;
  443. //
  444. // // scrollView 的 高度 ...
  445. // double scrollHeight = key.currentContext?.size?.height ?? 0;
  446. //
  447. // print("[scrollHeight]:$scrollHeight----------------------------");
  448. //
  449. // double position = scrollMenuController.position.maxScrollExtent;
  450. //
  451. // double currentOffset = scrollMenuController.offset;
  452. //
  453. // double getScrollTop(bool first) {
  454. // if (first) {
  455. // return scrollHeight;
  456. // } else {
  457. // // 不需要滚动的状态... 当前 所处的位置 需不需要 加上 bottomHeight...
  458. // if (currentOffset < bottomHeight) {
  459. // return position + appBarAndHeight + bottomHeight;
  460. // } else {
  461. // return position + appBarAndHeight;
  462. // }
  463. // }
  464. // }
  465. //
  466. // if (scrollMenuController.hasClients) {
  467. // Future.delayed(Duration(milliseconds: 30), () {
  468. // scrollMenuController.jumpTo(getScrollTop(first));
  469. // });
  470. // }
  471. // }
  472. //
  473. // // 后续修改为用这个...
  474. // void scroll() {
  475. // // SchedulerBinding.instance?.addPostFrameCallback((_) {
  476. // ////here the sublist is already build
  477. // // scrollMenuController
  478. // // .jumpTo(scrollMenuController.position.maxScrollExtent);
  479. // // print(
  480. // // "${scrollMenuController.position.minScrollExtent} - ${scrollMenuController.position.maxScrollExtent}");
  481. // // });
  482. // }
  483. // }
  484. //
  485. // // 自己内部传的 bean 用来鉴别 是什么 场景使用的 menuBar
  486. // class MenuIdentity {
  487. // String menuScene; // 当前的MenuBar 所属的场景在哪 比如 聊天 chat / 社区 social / 反馈 feedBack 等
  488. // int userId; // Chat的时候需要的userId
  489. //
  490. // MenuIdentity({required this.menuScene, required this.userId});
  491. //
  492. // Map<String, dynamic> toJson() {
  493. // final Map<String, dynamic> data = new Map<String, dynamic>();
  494. // data['menuScene'] = this.menuScene;
  495. // data['userId'] = this.userId;
  496. // return data;
  497. // }
  498. // }