PublishTweetPage.dart 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:io';
  4. import 'package:async/async.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:http/http.dart';
  7. import '../api/Api.dart';
  8. import 'package:http/http.dart' as http;
  9. import '../util/DataUtils.dart';
  10. import 'package:image_picker/image_picker.dart';
  11. class PublishTweetPage extends StatefulWidget {
  12. @override
  13. State<StatefulWidget> createState() {
  14. return new PublishTweetPageState();
  15. }
  16. }
  17. class PublishTweetPageState extends State<PublishTweetPage> {
  18. TextEditingController _controller = new TextEditingController();
  19. List<File> fileList = new List();
  20. Future<File> _imageFile;
  21. bool isLoading = false;
  22. String msg = "";
  23. Widget getBody() {
  24. var textField = new TextField(
  25. decoration: new InputDecoration(
  26. hintText: "说点什么吧~",
  27. hintStyle: new TextStyle(
  28. color: const Color(0xFF808080)
  29. ),
  30. border: new OutlineInputBorder(
  31. borderRadius: const BorderRadius.all(const Radius.circular(10.0))
  32. )
  33. ),
  34. maxLines: 6,
  35. maxLength: 150,
  36. controller: _controller,
  37. );
  38. var gridView = new Builder(
  39. builder: (ctx) {
  40. return new GridView.count(
  41. crossAxisCount: 4,
  42. children: new List.generate(fileList.length + 1, (index) {
  43. var content;
  44. if (index == 0) {
  45. // 添加图片按钮
  46. var addCell = new Center(
  47. child: new Image.asset('./images/ic_add_pics.png', width: 80.0, height: 80.0,)
  48. );
  49. content = new GestureDetector(
  50. onTap: () {
  51. // 添加图片
  52. pickImage(ctx);
  53. },
  54. child: addCell,
  55. );
  56. } else {
  57. // 被选中的图片
  58. content = new Center(
  59. child: new Image.file(fileList[index - 1], width: 80.0, height: 80.0, fit: BoxFit.cover,)
  60. );
  61. }
  62. return new Container(
  63. margin: const EdgeInsets.all(2.0),
  64. width: 80.0,
  65. height: 80.0,
  66. color: const Color(0xFFECECEC),
  67. child: content,
  68. );
  69. }),
  70. );
  71. },
  72. );
  73. var children = [
  74. new Text("提示:由于OSC的openapi限制,发布动弹的接口只支持上传一张图片,本项目可添加最多9张图片,但OSC只会接收最后一张图片。", style: new TextStyle(fontSize: 12.0),),
  75. textField,
  76. new Container(
  77. margin: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 0.0),
  78. height: 200.0,
  79. child: gridView
  80. )
  81. ];
  82. if (isLoading) {
  83. children.add(new Container(
  84. margin: const EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 0.0),
  85. child: new Center(
  86. child: new CircularProgressIndicator(),
  87. ),
  88. ));
  89. } else {
  90. children.add(new Container(
  91. margin: const EdgeInsets.fromLTRB(0.0, 20.0, 0.0, 0.0),
  92. child: new Center(
  93. child: new Text(msg),
  94. )
  95. ));
  96. }
  97. return new Container(
  98. padding: const EdgeInsets.all(5.0),
  99. child: new Column(
  100. children: children,
  101. ),
  102. );
  103. }
  104. // 相机拍照或者从图库选择图片
  105. pickImage(ctx) {
  106. // 如果已添加了9张图片,则提示不允许添加更多
  107. num size = fileList.length;
  108. if (size >= 9) {
  109. Scaffold.of(ctx).showSnackBar(new SnackBar(
  110. content: new Text("最多只能添加9张图片!"),
  111. ));
  112. return;
  113. }
  114. showModalBottomSheet<void>(context: context, builder: _bottomSheetBuilder);
  115. }
  116. Widget _bottomSheetBuilder(BuildContext context) {
  117. return new Container(
  118. height: 182.0,
  119. child: new Padding(
  120. padding: const EdgeInsets.fromLTRB(0.0, 30.0, 0.0, 30.0),
  121. child: new Column(
  122. children: [
  123. _renderBottomMenuItem("相机拍照", ImageSource.camera),
  124. new Divider(height: 2.0,),
  125. _renderBottomMenuItem("图库选择照片", ImageSource.gallery)
  126. ],
  127. ),
  128. )
  129. );
  130. }
  131. _renderBottomMenuItem(title, ImageSource source) {
  132. var item = new Container(
  133. height: 60.0,
  134. child: new Center(
  135. child: new Text(title)
  136. ),
  137. );
  138. return new InkWell(
  139. child: item,
  140. onTap: () {
  141. Navigator.of(context).pop();
  142. setState(() {
  143. _imageFile = ImagePicker.pickImage(source: source);
  144. });
  145. },
  146. );
  147. }
  148. sendTweet(ctx, token) async {
  149. if (token == null) {
  150. Scaffold.of(ctx).showSnackBar(new SnackBar(
  151. content: new Text("未登录!"),
  152. ));
  153. return;
  154. }
  155. String content = _controller.text;
  156. if (content == null || content.length == 0 || content.trim().length == 0) {
  157. Scaffold.of(ctx).showSnackBar(new SnackBar(
  158. content: new Text("请输入动弹内容!"),
  159. ));
  160. }
  161. try {
  162. Map<String, String> params = new Map();
  163. params['msg'] = content;
  164. params['access_token'] = token;
  165. var request = new MultipartRequest('POST', Uri.parse(Api.PUB_TWEET));
  166. request.fields.addAll(params);
  167. if (fileList != null && fileList.length > 0) {
  168. for (File f in fileList) {
  169. var stream = new http.ByteStream(
  170. DelegatingStream.typed(f.openRead()));
  171. var length = await f.length();
  172. var filename = f.path.substring(f.path.lastIndexOf("/") + 1);
  173. request.files.add(new http.MultipartFile(
  174. 'img', stream, length, filename: filename));
  175. }
  176. }
  177. setState(() {
  178. isLoading = true;
  179. });
  180. var response = await request.send();
  181. response.stream.transform(utf8.decoder).listen((value) {
  182. print(value);
  183. if (value != null) {
  184. var obj = json.decode(value);
  185. var error = obj['error'];
  186. setState(() {
  187. if (error != null && error == '200') {
  188. // 成功
  189. setState(() {
  190. isLoading = false;
  191. msg = "发布成功";
  192. fileList.clear();
  193. });
  194. _controller.clear();
  195. } else {
  196. setState(() {
  197. isLoading = false;
  198. msg = "发布失败:$error";
  199. });
  200. }
  201. });
  202. }
  203. });
  204. } catch (exception) {
  205. print(exception);
  206. }
  207. }
  208. @override
  209. Widget build(BuildContext context) {
  210. return new Scaffold(
  211. appBar: new AppBar(
  212. title: new Text("发布动弹", style: new TextStyle(color: Colors.white)),
  213. iconTheme: new IconThemeData(color: Colors.white),
  214. actions: <Widget>[
  215. new Builder(
  216. builder: (ctx) {
  217. return new IconButton(icon: new Icon(Icons.send), onPressed: () {
  218. // 发送动弹
  219. DataUtils.isLogin().then((isLogin) {
  220. if (isLogin) {
  221. return DataUtils.getAccessToken();
  222. } else {
  223. return null;
  224. }
  225. }).then((token) {
  226. sendTweet(ctx, token);
  227. });
  228. });
  229. },
  230. )
  231. ],
  232. ),
  233. body: new FutureBuilder(
  234. future: _imageFile,
  235. builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
  236. if (snapshot.connectionState == ConnectionState.done &&
  237. snapshot.data != null && _imageFile != null) {
  238. fileList.add(snapshot.data);
  239. _imageFile = null;
  240. }
  241. return getBody();
  242. },
  243. ),
  244. );
  245. }
  246. }