PublishTweetPage.dart 7.2 KB

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