home.dart 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import 'package:dio/dio.dart';
  2. import 'package:flutter/cupertino.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:mvvm_flutter/di/modules.dart';
  5. import 'package:mvvm_flutter/helper/toast.dart';
  6. import 'package:mvvm_flutter/helper/widgetutils.dart';
  7. import 'package:mvvm_flutter/model/repository.dart';
  8. import 'package:provide/provide.dart';
  9. import 'package:rxdart/rxdart.dart';
  10. class HomeWidget extends StatefulWidget {
  11. @override
  12. State<StatefulWidget> createState() {
  13. return _HomeState(provideHomeViewModel());
  14. }
  15. }
  16. /**
  17. * View
  18. */
  19. class _HomeState extends State<HomeWidget>
  20. with SingleTickerProviderStateMixin<HomeWidget> {
  21. final HomeViewModel _viewModel;
  22. final CompositeSubscription _subscriptions = CompositeSubscription();
  23. AnimationController _controller;
  24. Animation<double> _animation;
  25. _HomeState(this._viewModel) {
  26. providers.provideValue(_viewModel);
  27. }
  28. @override
  29. void initState() {
  30. super.initState();
  31. _controller = AnimationController(
  32. vsync: this, duration: const Duration(milliseconds: 300));
  33. _animation = Tween(begin: _viewModel.btnWidth, end: 48.0).animate(_controller)
  34. ..addListener(() {
  35. _viewModel.btnWidth = _animation.value;
  36. });
  37. }
  38. _login() {
  39. final s = _viewModel.login().doOnListen(() {
  40. _controller.forward();
  41. }).doOnDone(() {
  42. _controller.reverse();
  43. }).listen((_) {
  44. //success
  45. Toast.show("login success",context,type: Toast.SUCCESS);
  46. }, onError: (e) {
  47. //error
  48. dispatchFailure(context, e);
  49. });
  50. _subscriptions.add(s);
  51. }
  52. @override
  53. void dispose() {
  54. _controller.dispose();
  55. _subscriptions.dispose();
  56. super.dispose();
  57. }
  58. @override
  59. Widget build(BuildContext context) {
  60. return Scaffold(
  61. appBar: AppBar(
  62. title: const Text("MVVM-Flutter"),
  63. ),
  64. body: Material(
  65. child: Column(
  66. children: <Widget>[
  67. TextField(
  68. keyboardType: TextInputType.text,
  69. decoration: InputDecoration(
  70. contentPadding: EdgeInsets.all(10.0),
  71. icon: Icon(Icons.person),
  72. labelText: '账号',
  73. ),
  74. autofocus: false,
  75. onChanged: (str) => _viewModel.username = str,
  76. ),
  77. TextField(
  78. obscureText: true,
  79. keyboardType: TextInputType.number,
  80. decoration: InputDecoration(
  81. contentPadding: EdgeInsets.all(10.0),
  82. icon: Icon(Icons.lock),
  83. labelText: '密码',
  84. ),
  85. autofocus: false,
  86. onChanged: (str) => _viewModel.password = str,
  87. ),
  88. const Padding(
  89. padding: EdgeInsets.only(top: 30.0),
  90. ),
  91. Provide<HomeViewModel>(
  92. builder: (BuildContext context, Widget child,
  93. HomeViewModel value) =>
  94. CupertinoButton(
  95. onPressed: value.loading?null:_login,
  96. pressedOpacity: 0.8,
  97. child: Container(
  98. alignment: Alignment.center,
  99. width: value.btnWidth,
  100. height: 48,
  101. decoration: BoxDecoration(
  102. borderRadius: BorderRadius.all(Radius.circular(30.0)),
  103. gradient: LinearGradient(colors: [
  104. Color(0xFF686CF2),
  105. Color(0xFF0E5CFF),
  106. ]),
  107. boxShadow: [
  108. BoxShadow(
  109. color: Color(0x4D5E56FF),
  110. offset: Offset(0.0, 4.0),
  111. blurRadius: 13.0)
  112. ]),
  113. child: _buildChild(value),
  114. ),
  115. ),
  116. ),
  117. const Text(
  118. "Response:",
  119. style: TextStyle(fontSize: 18),
  120. textAlign: TextAlign.start,
  121. ),
  122. Expanded(
  123. child: Container(
  124. constraints: BoxConstraints(minWidth: double.infinity),
  125. margin: EdgeInsets.fromLTRB(12, 12, 12, 0),
  126. padding: EdgeInsets.all(5.0),
  127. decoration:
  128. BoxDecoration(border: Border.all(color: Colors.blue)),
  129. child: Provide<HomeViewModel>(
  130. builder: (BuildContext context, Widget child,
  131. HomeViewModel value) =>
  132. Text(value.response),
  133. ),
  134. ),
  135. )
  136. ],
  137. ),
  138. ),
  139. );
  140. }
  141. Widget _buildChild(HomeViewModel value) {
  142. if (value.loading) {
  143. return const CircularProgressIndicator();
  144. } else {
  145. return const FittedBox(
  146. fit: BoxFit.scaleDown,
  147. child: const Text(
  148. '使用GitHub账号登录',
  149. maxLines: 1,
  150. textAlign: TextAlign.center,
  151. overflow: TextOverflow.fade,
  152. style: const TextStyle(
  153. fontWeight: FontWeight.bold, fontSize: 16.0, color: Colors.white),
  154. ),
  155. );
  156. }
  157. }
  158. }
  159. /**
  160. * ViewModel
  161. */
  162. class HomeViewModel extends ChangeNotifier {
  163. final GithubRepo _repo; //数据仓库
  164. String username = ""; //账号
  165. String password = ""; //密码
  166. bool _loading = false; // 加载中
  167. String _response = ""; //响应数据
  168. String get response => _response;
  169. set response(String response) {
  170. _response = response;
  171. notifyListeners();
  172. }
  173. bool get loading => _loading;
  174. double _btnWidth = 295.0;
  175. double get btnWidth => _btnWidth;
  176. set btnWidth(double btnWidth) {
  177. _btnWidth = btnWidth;
  178. notifyListeners();
  179. }
  180. set loading(bool loading) {
  181. _loading = loading;
  182. notifyListeners();
  183. }
  184. HomeViewModel(this._repo);
  185. /**
  186. * 调用model层的方法进行登录
  187. * doOnData : 请求成功时,处理响应数据
  188. * doOnError : 请求失败时,处理错误
  189. * doOnListen : 开始时loading为true,通知ui更新
  190. * doOnDone : 结束时loading为false,通知ui更新
  191. */
  192. Observable login() => _repo
  193. .login(username, password)
  194. .doOnData((r) => response = r.toString())
  195. .doOnError((e, stacktrace) {
  196. if (e is DioError) {
  197. response = e.response.data.toString();
  198. }
  199. })
  200. .doOnListen(() => loading = true)
  201. .doOnDone(() => loading = false);
  202. }