diff --git a/lib/constants/api.dart b/lib/constants/api.dart index 86c173b4..6ebee7bc 100644 --- a/lib/constants/api.dart +++ b/lib/constants/api.dart @@ -1,9 +1,14 @@ class API { - static const String host = 'http://192.168.31.129:9001/app'; + static const String host = 'http://192.168.2.201:8804/IntelligentCommunity'; + static String get resource => '$host/static'; static const int networkTimeOut = 10000; static _Login login = _Login(); } class _Login { - String get a => ''; + /// 获取手机验证码 + String get sendSMSCode => '/login/sendMMSLogin'; + + /// 通过验证码短信登陆 + String get loginBySMS => '/login/loginSMSUser'; } diff --git a/lib/extensions/page_router.dart b/lib/extensions/page_router.dart index 28943a29..5275c594 100644 --- a/lib/extensions/page_router.dart +++ b/lib/extensions/page_router.dart @@ -1,7 +1,6 @@ - import 'package:flutter/material.dart'; import 'package:get/get.dart'; -extension PageExt on Widget{ - Function get to => (){Get.to(this);}; -} \ No newline at end of file +extension PageExt on Widget { + Function get to => () => Get.to(this); +} diff --git a/lib/main.dart b/lib/main.dart index 6c7cb2d1..a93ec6d3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,22 +1,27 @@ -import 'package:akuCommunity/pages/tab_navigator.dart'; +import 'package:akuCommunity/pages/splash/splash_page.dart'; import 'package:akuCommunity/provider/user_provider.dart'; import 'package:akuCommunity/utils/developer_util.dart'; -import 'package:amap_map_fluttify/amap_map_fluttify.dart'; +import 'package:akuCommunity/utils/hive_store.dart'; +import 'package:amap_location_fluttify/amap_location_fluttify.dart'; import 'package:ani_route/ani_route.dart'; import 'package:bot_toast/bot_toast.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:fluwx/fluwx.dart'; import 'package:get/get.dart'; +import 'package:hive/hive.dart'; +import 'package:hive_flutter/hive_flutter.dart'; import 'package:provider/provider.dart'; import 'package:akuCommunity/provider/cart.dart'; import 'package:flutter_picker/flutter_picker.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; -void main() { +void main() async { WidgetsFlutterBinding.ensureInitialized(); ARoute.init(true); + await Hive.initFlutter(); + await HiveStore.init(); AmapLocation.instance.init(iosKey: 'ios key'); DeveloperUtil.setDev(true); runApp(MyApp()); @@ -50,7 +55,7 @@ class _MyAppState extends State { primarySwatch: Colors.yellow, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: TabNavigator(), + home: SplashPage(), //国际化支持 localizationsDelegates: [ PickerLocalizationsDelegate.delegate, diff --git a/lib/model/user/user_info_model.dart b/lib/model/user/user_info_model.dart new file mode 100644 index 00000000..14d53268 --- /dev/null +++ b/lib/model/user/user_info_model.dart @@ -0,0 +1,64 @@ +class UserInfoModel { + Data data; + String message; + bool status; + + UserInfoModel({this.data, this.message, this.status}); + + UserInfoModel.fromJson(Map json) { + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + message = json['message']; + status = json['status']; + } + + Map toJson() { + final Map data = new Map(); + if (this.data != null) { + data['data'] = this.data.toJson(); + } + data['message'] = this.message; + data['status'] = this.status; + return data; + } +} + +class Data { + int id; + List imgUrls; + String name; + String nickName; + String tel; + int sex; + String birthday; + + Data( + {this.id, + this.imgUrls, + this.name, + this.nickName, + this.tel, + this.sex, + this.birthday}); + + Data.fromJson(Map json) { + id = json['id']; + imgUrls = json['imgUrls'].cast(); + name = json['name']; + nickName = json['nickName']; + tel = json['tel']; + sex = json['sex']; + birthday = json['birthday']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['imgUrls'] = this.imgUrls; + data['name'] = this.name; + data['nickName'] = this.nickName; + data['tel'] = this.tel; + data['sex'] = this.sex; + data['birthday'] = this.birthday; + return data; + } +} diff --git a/lib/pages/personal/personal_page.dart b/lib/pages/personal/personal_page.dart index e141dec9..b7676305 100644 --- a/lib/pages/personal/personal_page.dart +++ b/lib/pages/personal/personal_page.dart @@ -11,7 +11,7 @@ import 'package:akuCommunity/pages/things_page/fixed_submit_page.dart'; import 'package:akuCommunity/pages/visitor_access_page/visitor_access_page.dart'; import 'package:akuCommunity/painters/user_bottom_bar_painter.dart'; import 'package:akuCommunity/provider/user_provider.dart'; -import 'package:akuCommunity/utils/net_util.dart'; +import 'package:akuCommunity/utils/network/net_util.dart'; import 'package:akuCommunity/widget/grid_buttons.dart'; import 'package:ani_route/ani_route.dart'; import 'package:flutter/material.dart'; @@ -137,8 +137,7 @@ class _PersonalIndexState extends State ), InkWell( onTap: () { - if (!userProvider.isSigned) - ARoute.push(context, SignInPage()); + if (!userProvider.isSigned) SignInPage().to(); }, child: Container( margin: EdgeInsets.only(left: 16.w), diff --git a/lib/pages/sign/sign_func.dart b/lib/pages/sign/sign_func.dart new file mode 100644 index 00000000..2aafac3f --- /dev/null +++ b/lib/pages/sign/sign_func.dart @@ -0,0 +1,24 @@ +import 'package:akuCommunity/constants/api.dart'; +import 'package:akuCommunity/utils/network/base_model.dart'; +import 'package:akuCommunity/utils/network/net_util.dart'; +import 'package:bot_toast/bot_toast.dart'; +import 'package:dio/dio.dart'; + +class SignFunc { + static Future sendMessageCode(String phone) async { + BaseModel baseModel = await NetUtil().post( + API.login.sendSMSCode, + params: {'tel': phone}, + showMessage: true, + ); + return baseModel; + } + + static Future login(String phone, String code) async { + Response response = await NetUtil().dio.post( + API.login.loginBySMS, + data: {'tel': phone, 'code': code}, + ); + return response; + } +} diff --git a/lib/pages/sign/sign_in_page.dart b/lib/pages/sign/sign_in_page.dart index a269404c..d15a266d 100644 --- a/lib/pages/sign/sign_in_page.dart +++ b/lib/pages/sign/sign_in_page.dart @@ -1,15 +1,15 @@ import 'dart:async'; -import 'dart:math'; import 'dart:ui'; import 'package:akuCommunity/pages/setting_page/agreement_page/agreement_page.dart'; import 'package:akuCommunity/pages/setting_page/agreement_page/privacy_page.dart'; -import 'package:akuCommunity/pages/sign/user_authentication_page.dart'; +import 'package:akuCommunity/pages/sign/sign_func.dart'; import 'package:akuCommunity/extensions/num_ext.dart'; import 'package:akuCommunity/const/resource.dart'; import 'package:ani_route/ani_route.dart'; import 'package:bot_toast/bot_toast.dart'; import 'package:common_utils/common_utils.dart'; +import 'package:dio/dio.dart'; import 'package:flustars/flustars.dart' show TextUtil; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; @@ -17,7 +17,7 @@ import 'package:akuCommunity/utils/headers.dart'; import 'package:akuCommunity/base/base_style.dart'; import 'package:akuCommunity/base/assets_image.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:get/get.dart'; +import 'package:get/get.dart' hide Response; import 'package:velocity_x/velocity_x.dart'; class SignInPage extends StatefulWidget { @@ -51,67 +51,62 @@ class _SignInPageState extends State { ); } - InkWell _inkWellLogin() { - return InkWell( - onTap: () { - if (TextUtil.isEmpty(_phone.text)) - BotToast.showText(text: '手机号不能为空'); - else if (TextUtil.isEmpty(_code.text)) - BotToast.showText(text: '验证码不能为空'); - else { - showCupertinoDialog( - context: context, - builder: (context) { - return CupertinoAlertDialog( - title: Text('点击登录即表示您已阅读并同意'), - content: Text( - '''点击登录即表示您已阅读并同意《小蜜蜂用户协议》《小蜜蜂隐私政策》(特别是免除或限制责任、管辖等粗体下划线标注的条款)。如您不同意上述协议的任何条款,您应立即停止登录及使用本软件及服务。'''), - actions: [ - CupertinoDialogAction( - child: Text('同意'), - onPressed: () { - Future.delayed( - Duration(milliseconds: 1000 + Random().nextInt(500)), - () { - Get.back(); - (_phone.text == '18067170899') && - (_code.text == '123456') - ? ARoute.push(context, UserAuthenticationPage()) - : BotToast.showText(text: '账号或密码错误!'); - }, - ); - }, - ), - CupertinoDialogAction( - child: Text('拒绝'), - onPressed: () { - Get.back(); - }, - ), - ], - ); - }, - ); - } + Future _showLoginVerify() async { + return await showCupertinoDialog( + barrierDismissible: false, + context: context, + builder: (context) { + return CupertinoAlertDialog( + title: Text('点击登录即表示您已阅读并同意'), + content: Text( + '''点击登录即表示您已阅读并同意《小蜜蜂用户协议》《小蜜蜂隐私政策》(特别是免除或限制责任、管辖等粗体下划线标注的条款)。如您不同意上述协议的任何条款,您应立即停止登录及使用本软件及服务。'''), + actions: [ + CupertinoDialogAction( + child: Text('同意'), + onPressed: () => Get.back(result: true), + ), + CupertinoDialogAction( + child: Text('拒绝'), + onPressed: () => Get.back(result: false), + ), + ], + ); }, - child: Container( - alignment: Alignment.center, + ); + } + + _parseLogin(bool result) async { + if (!result) return; + BotToast.showLoading(); + Response response = await SignFunc.login(_phone.text, _code.text); + + BotToast.cleanAll(); + if (response.data['status']) { + } else { + BotToast.showText(text: response.data['message']); + } + } + + Widget _inkWellLogin() { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 80.w), + child: MaterialButton( + onPressed: () async { + if (TextUtil.isEmpty(_phone.text)) + BotToast.showText(text: '手机号不能为空'); + else if (TextUtil.isEmpty(_code.text)) + BotToast.showText(text: '验证码不能为空'); + else { + bool result = await _showLoginVerify(); + _parseLogin(result); + } + }, height: 89.w, - width: 586.w, - padding: EdgeInsets.only(top: 25.w, bottom: 24.w), - margin: EdgeInsets.symmetric(horizontal: 82.w), - decoration: BoxDecoration( - color: Color(0xffffc40c), - borderRadius: BorderRadius.all(Radius.circular(36)), - ), - child: Text( - '登录', - style: TextStyle( - fontWeight: FontWeight.w600, - fontSize: BaseStyle.fontSize28, - color: ktextPrimary, - ), - ), + shape: StadiumBorder(), + elevation: 0, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + child: '登陆'.text.bold.size(28.sp).color(ktextPrimary).make(), + color: kPrimaryColor, ), ); } @@ -182,107 +177,101 @@ class _SignInPageState extends State { return Scaffold( backgroundColor: Colors.white, appBar: _appBar, - body: SingleChildScrollView( + body: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + FocusScope.of(context).requestFocus(FocusNode()); + }, child: Container( - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - FocusScope.of(context).requestFocus(FocusNode()); - }, - child: Container( - color: Colors.white, - child: ListView( - shrinkWrap: true, - children: [ - SizedBox( - height: 153.w, - ), - _containerImage(), - SizedBox(height: 16.w), - Container( - alignment: Alignment.center, - child: Text( - '欢迎登录小蜜蜂', - style: TextStyle( - fontWeight: FontWeight.w600, - fontSize: BaseStyle.fontSize38, - color: ktextPrimary), - ), - ), - SizedBox(height: 89.w), - _buildTextField( - hint: '请输入手机号', - controller: _phone, - prefix: Image.asset( - R.ASSETS_IMAGES_PHONE_LOGO_PNG, - height: 50.w, - width: 50.w, + color: Colors.white, + child: ListView( + children: [ + 153.hb, + _containerImage(), + 16.hb, + Container( + alignment: Alignment.center, + child: Text( + '欢迎登录小蜜蜂', + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: BaseStyle.fontSize38, + color: ktextPrimary), + ), + ), + 89.hb, + _buildTextField( + hint: '请输入手机号', + controller: _phone, + prefix: Image.asset( + R.ASSETS_IMAGES_PHONE_LOGO_PNG, + height: 50.w, + width: 50.w, + ), + ), + 26.hb, + _buildTextField( + prefix: Image.asset( + R.ASSETS_IMAGES_CODE_LOGO_PNG, + height: 50.w, + width: 50.w, + ), + hint: '请输入验证码', + controller: _code, + suffix: MaterialButton( + height: 82.w, + shape: StadiumBorder(), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + child: Text( + _timer?.isActive ?? false + ? '${60 - _timer.tick}' + : '获取验证码', + style: TextStyle( + color: BaseStyle.color999999, + fontSize: BaseStyle.fontSize28, + fontWeight: FontWeight.w500, ), ), - 26.hb, - _buildTextField( - prefix: Image.asset( - R.ASSETS_IMAGES_CODE_LOGO_PNG, - height: 50.w, - width: 50.w, - ), - hint: '请输入验证码', - controller: _code, - suffix: MaterialButton( - height: 82.w, - shape: StadiumBorder(), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + onPressed: _canGetCode + ? () { + SignFunc.sendMessageCode(_phone.text); + startTick(); + } + : null, + ), + ), + SizedBox(height: 59.w), + _inkWellLogin(), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FlatButton( + padding: EdgeInsets.zero, + onPressed: () { + ARoute.push(context, AgreementPage()); + }, child: Text( - _timer?.isActive ?? false - ? '${60 - _timer.tick}' - : '获取验证码', + '《小蜜蜂用户协议》', style: TextStyle( - color: BaseStyle.color999999, - fontSize: BaseStyle.fontSize28, - fontWeight: FontWeight.w500, + color: Colors.lightBlue, ), - ), - onPressed: _canGetCode - ? () { - startTick(); - } - : null, - ), - ), - SizedBox(height: 59.w), - _inkWellLogin(), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FlatButton( - padding: EdgeInsets.zero, - onPressed: () { - ARoute.push(context, AgreementPage()); - }, - child: Text( - '《小蜜蜂用户协议》', - style: TextStyle( - color: Colors.lightBlue, - ), - )), - SizedBox(width: 15.w), - FlatButton( - padding: EdgeInsets.zero, - onPressed: () { - ARoute.push(context, PrivacyPage()); - }, - child: Text( - '《小蜜蜂隐私政策》', - style: TextStyle( - color: Colors.lightBlue, - ), - )) - ], - ), + )), + SizedBox(width: 15.w), + FlatButton( + padding: EdgeInsets.zero, + onPressed: () { + ARoute.push(context, PrivacyPage()); + }, + child: Text( + '《小蜜蜂隐私政策》', + style: TextStyle( + color: Colors.lightBlue, + ), + )) ], - )), - ), - ), + ), + ], + )), ), ); } diff --git a/lib/pages/splash/splash_page.dart b/lib/pages/splash/splash_page.dart new file mode 100644 index 00000000..115f3156 --- /dev/null +++ b/lib/pages/splash/splash_page.dart @@ -0,0 +1,39 @@ +import 'package:akuCommunity/pages/tab_navigator.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:velocity_x/velocity_x.dart'; + +//TODO splashPage +class SplashPage extends StatefulWidget { + SplashPage({Key key}) : super(key: key); + + @override + _SplashPageState createState() => _SplashPageState(); +} + +class _SplashPageState extends State { + Future _initOp() async { + await Future.delayed(Duration(seconds: 2)); + } + + @override + void initState() { + super.initState(); + _initOp().then((value) => Get.offAll(TabNavigator())); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SizedBox( + height: 80, + width: 80, + child: Placeholder(), + ).centered(), + bottomNavigationBar: SizedBox( + child: CircularProgressIndicator().centered(), + height: 100, + ), + ); + } +} diff --git a/lib/provider/user_provider.dart b/lib/provider/user_provider.dart index 453b878f..fbcf4580 100644 --- a/lib/provider/user_provider.dart +++ b/lib/provider/user_provider.dart @@ -1,10 +1,30 @@ +import 'package:akuCommunity/utils/hive_store.dart'; import 'package:flutter/material.dart'; -class UserProvider extends ChangeNotifier{ + +class UserProvider extends ChangeNotifier { //登录状态管理 - bool _isSigned =false; + bool _isSigned = false; get isSigned => _isSigned; - setisSigned(bool state){ - _isSigned=state; + setisSigned(bool state) { + _isSigned = state; notifyListeners(); } -} \ No newline at end of file + + bool _isLogin = false; + bool get isLogin => _isLogin; + setLogin(bool state, String token) { + _isLogin = state; + HiveStore.appBox.put('token', token); + notifyListeners(); + } + + logout() { + _isLogin = false; + _token = null; + HiveStore.appBox.delete('token'); + notifyListeners(); + } + + String _token; + String get token => _token ?? ''; +} diff --git a/lib/utils/hive_store.dart b/lib/utils/hive_store.dart new file mode 100644 index 00000000..724d2b4c --- /dev/null +++ b/lib/utils/hive_store.dart @@ -0,0 +1,9 @@ +import 'package:hive/hive.dart'; + +class HiveStore { + static Box _appBox; + static Box get appBox => _appBox; + static init() async { + _appBox = await Hive.openBox('app'); + } +} diff --git a/lib/utils/logger/logger_dio_err.dart b/lib/utils/logger/logger_dio_err.dart index aa0a3ed4..2385e987 100644 --- a/lib/utils/logger/logger_dio_err.dart +++ b/lib/utils/logger/logger_dio_err.dart @@ -1,4 +1,5 @@ import 'package:akuCommunity/extensions/num_ext.dart'; +import 'package:common_utils/common_utils.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:velocity_x/velocity_x.dart'; @@ -15,6 +16,7 @@ class LoggerDioErr extends StatelessWidget { color: Colors.red[100], onPressed: () {}, child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ @@ -31,6 +33,7 @@ class LoggerDioErr extends StatelessWidget { ), ], ), + error.response.headers['date'].first.toString().text.make(), error.message.text.sm.light.make(), ], ), diff --git a/lib/utils/network/base_model.dart b/lib/utils/network/base_model.dart new file mode 100644 index 00000000..0afc4854 --- /dev/null +++ b/lib/utils/network/base_model.dart @@ -0,0 +1,20 @@ +class BaseModel { + int code; + String message; + bool status; + dynamic data; + BaseModel({ + this.code, + this.message, + this.data, + this.status, + }); + + BaseModel.err({this.message = '未知错误', this.status = false}); + + BaseModel.fromJson(Map json) { + message = json['message'] ?? ''; + data = json['data'] ?? null; + status = json['status'] ?? false; + } +} \ No newline at end of file diff --git a/lib/utils/network/login_model.dart b/lib/utils/network/login_model.dart new file mode 100644 index 00000000..d0d7fc9e --- /dev/null +++ b/lib/utils/network/login_model.dart @@ -0,0 +1,21 @@ +class LoginModel { + String message; + String token; + bool status; + + LoginModel({this.message, this.token, this.status}); + + LoginModel.fromJson(Map json) { + message = json['message']; + token = json['token']; + status = json['status']; + } + + Map toJson() { + final Map data = new Map(); + data['message'] = this.message; + data['token'] = this.token; + data['status'] = this.status; + return data; + } +} diff --git a/lib/utils/net_util.dart b/lib/utils/network/net_util.dart similarity index 65% rename from lib/utils/net_util.dart rename to lib/utils/network/net_util.dart index 1478cd82..7391bc61 100644 --- a/lib/utils/net_util.dart +++ b/lib/utils/network/net_util.dart @@ -1,6 +1,5 @@ -import 'dart:convert'; - import 'package:akuCommunity/utils/logger/logger_data.dart'; +import 'package:akuCommunity/utils/network/base_model.dart'; import 'package:bot_toast/bot_toast.dart'; import 'package:dio/dio.dart'; @@ -14,6 +13,8 @@ class NetUtil { factory NetUtil() => _netUtil; + Dio get dio => _dio; + NetUtil._internal() { _logger = Logger( printer: PrettyPrinter( @@ -21,7 +22,7 @@ class NetUtil { errorMethodCount: 4, )); BaseOptions options = BaseOptions( - baseUrl: API.host, + baseUrl: '${API.host}/app', connectTimeout: API.networkTimeOut, receiveTimeout: API.networkTimeOut, sendTimeout: API.networkTimeOut, @@ -39,6 +40,7 @@ class NetUtil { Future get( String path, { Map params, + bool showMessage = false, }) async { try { Response res = await _dio.get(path, queryParameters: params); @@ -49,7 +51,33 @@ class NetUtil { 'data': res.data, }); LoggerData.addData(res); - return BaseModel.fromJson(res.data); + BaseModel baseModel = BaseModel.fromJson(res.data); + _parseRequestError(baseModel, showMessage: showMessage); + return baseModel; + } on DioError catch (e) { + _parseErr(e); + } + return BaseModel.err(); + } + + Future post( + String path, { + Map params, + bool showMessage = false, + }) async { + try { + Response res = await _dio.post(path, data: params); + _logger.v({ + 'path': res.request.path, + 'header': res.request.headers, + 'params': res.request.queryParameters, + 'data': res.data, + }); + LoggerData.addData(res); + BaseModel baseModel = BaseModel.fromJson(res.data); + _parseRequestError(baseModel, showMessage: showMessage); + + return baseModel; } on DioError catch (e) { _parseErr(e); } @@ -82,29 +110,10 @@ class NetUtil { break; } } -} - -class BaseModel { - int code; - String message; - dynamic data; - BaseModel({ - this.code, - this.message, - this.data, - }); - - BaseModel.err({this.message = '未知错误', this.code = 0}); - factory BaseModel.fromMap(Map map) { - if (map == null) return null; - return BaseModel( - code: map['code'] ?? 0, - message: map['message'] ?? '', - data: map['data'], - ); + _parseRequestError(BaseModel model, {bool showMessage = false}) { + if (!model.status || showMessage) { + BotToast.showText(text: model.message); + } } - - factory BaseModel.fromJson(String source) => - BaseModel.fromMap(json.decode(source)); }