diff --git a/lib/app/splash_page.dart b/lib/app/splash_page.dart index cdf870e..5d8ef98 100644 --- a/lib/app/splash_page.dart +++ b/lib/app/splash_page.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:wow_english/common/extension/string_extension.dart'; -import 'package:wow_english/network/basic_configuration.dart'; +import 'package:wow_english/network/basic_configuration.dart.txt'; import 'package:wow_english/route/route.dart'; @@ -59,4 +59,4 @@ class _TransitionViewState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/common/request/api_response/api_response_entity.dart b/lib/common/request/api_response/api_response_entity.dart new file mode 100644 index 0000000..ec66435 --- /dev/null +++ b/lib/common/request/api_response/api_response_entity.dart @@ -0,0 +1,20 @@ +import 'dart:convert'; + +import 'api_response_entity.g.dart'; + +class ApiResponse { + int? code; + String? msg; + T? data; + + ApiResponse(); + + factory ApiResponse.fromJson(Map json) => $ApiResponseFromJson(json); + + Map toJson() => $ApiResponseToJson(this); + + @override + String toString() { + return jsonEncode(this); + } +} diff --git a/lib/common/request/api_response/api_response_entity.g.dart b/lib/common/request/api_response/api_response_entity.g.dart new file mode 100644 index 0000000..a5ff0fe --- /dev/null +++ b/lib/common/request/api_response/api_response_entity.g.dart @@ -0,0 +1,36 @@ +import 'package:flutter/foundation.dart'; + +import '../../../generated/json/base/json_convert_content.dart'; +import 'api_response_entity.dart'; + +ApiResponse $ApiResponseFromJson(Map json) { + final ApiResponse apiResponseEntity = ApiResponse(); + final int? code = jsonConvert.convert(json['code']); + if (code != null) { + apiResponseEntity.code = code; + } + final String? msg = jsonConvert.convert(json['msg']); + if (msg != null) { + apiResponseEntity.msg = msg; + } + String type = T.toString(); + T? data; + if (kDebugMode) { + print("type:$type"); + } + if (json['data'] != null) { + data = jsonConvert.convert(json['data']); + } + if (data != null) { + apiResponseEntity.data = data; + } + return apiResponseEntity; +} + +Map $ApiResponseToJson(ApiResponse entity) { + final Map data = {}; + data['code'] = entity.code; + data['msg'] = entity.msg; + data['data'] = entity.data; + return data; +} diff --git a/lib/common/request/api_response/raw_data.dart b/lib/common/request/api_response/raw_data.dart new file mode 100644 index 0000000..ddca7d7 --- /dev/null +++ b/lib/common/request/api_response/raw_data.dart @@ -0,0 +1,3 @@ +class RawData{ + dynamic value; +} \ No newline at end of file diff --git a/lib/common/request/apis.dart b/lib/common/request/apis.dart new file mode 100644 index 0000000..d63624a --- /dev/null +++ b/lib/common/request/apis.dart @@ -0,0 +1,25 @@ +class Apis { + /// app初始化配置信息 + // GET /system/app/config + // 接口地址:https://app.apifox.com/link/project/2684751/apis/api-89897678 + static const String appConfig = 'system/app/config'; + + /// 登陆 + static const String login = 'login'; + + /// 登出 + static const String logout = 'logout'; + + /// 发送验证码 + static const String sendSmsCode = 'system/send/code'; + + /// 课程模块 + // GET /home/courseModule + // 接口地址:https://app.apifox.com/link/project/2684751/apis/api-89897663 + static const String courseModule = 'home/courseModule'; + + /// 课程列表 + // GET /home/courseLesson + // 接口地址:https://app.apifox.com/link/project/2684751/apis/api-89897662 + static const String courseLesson = 'home/courseLesson'; +} diff --git a/lib/common/request/config.dart b/lib/common/request/config.dart new file mode 100644 index 0000000..aa3bba0 --- /dev/null +++ b/lib/common/request/config.dart @@ -0,0 +1,6 @@ +///request config +class RequestConfig { + static String baseUrl = 'http://wow-app.dev.kouyuxingqiu.com/'; + static const connectTimeout = Duration(seconds: 15); + static const successCode = 200; +} diff --git a/lib/common/request/dao/user_dao.dart b/lib/common/request/dao/user_dao.dart new file mode 100644 index 0000000..13394ae --- /dev/null +++ b/lib/common/request/dao/user_dao.dart @@ -0,0 +1,32 @@ +import '../../../models/user_entity.dart'; +import '../api_response/api_response_entity.dart'; +import '../apis.dart'; +import '../exception.dart'; +import '../request_client.dart'; + +class UserDao { + static loginByPassword( + phoneNumber, + password, + Function(ApiResponse)? onResponse, + bool Function(ApiException)? onError, + ) async { + /*await DioUtil().requestData( + HttpMethod.post, + Api.login, + data: { + 'phoneNum':phoneNumber, + 'type':'pwd', + 'password':password}, + successCallBack: (data){ + emitter(LoginResultChangeState(true)); + }, + errorCallBack: (error){ + emitter(LoginResultChangeState(false)); + });*/ + var params = {'phoneNum': phoneNumber, 'type': 'pwd', 'password': password}; + await requestClient.post(Apis.login, data: params, onResponse: onResponse, onError: onError); + } + + static loginBySmsCode(phoneNumber, smsCode) {} +} diff --git a/lib/common/request/exception.dart b/lib/common/request/exception.dart new file mode 100644 index 0000000..a23c76a --- /dev/null +++ b/lib/common/request/exception.dart @@ -0,0 +1,90 @@ +import 'package:dio/dio.dart'; + +import 'api_response/api_response_entity.dart'; + +class ApiException implements Exception { + static const unknownException = "未知错误"; + final String? message; + final int? code; + String? stackInfo; + + ApiException([this.code, this.message]); + + factory ApiException.fromDioError(DioException error) { + switch (error.type) { + case DioExceptionType.connectionTimeout: + return BadRequestException(-1, "连接超时"); + case DioExceptionType.sendTimeout: + return BadRequestException(-1, "请求超时"); + case DioExceptionType.receiveTimeout: + return BadRequestException(-1, "响应超时"); + case DioExceptionType.badCertificate: + return BadRequestException(-1, "证书错误"); + case DioExceptionType.badResponse: + return BadRequestException(-1, "返回错误"); + case DioExceptionType.cancel: + return BadRequestException(-1, "请求取消"); + case DioExceptionType.connectionError: + return BadRequestException(-1, "连接错误"); + case DioExceptionType.unknown: + try { + /// http错误码带业务错误信息 + ApiResponse apiResponse = ApiResponse.fromJson(error.response?.data); + if (apiResponse.code != null) { + return ApiException(apiResponse.code, apiResponse.msg); + } + + int? errCode = error.response?.statusCode; + switch (errCode) { + case 400: + return BadRequestException(errCode, "请求语法错误"); + case 401: + return UnauthorisedException(errCode!, "没有权限"); + case 403: + return UnauthorisedException(errCode!, "服务器拒绝执行"); + case 404: + return UnauthorisedException(errCode!, "无法连接服务器"); + case 405: + return UnauthorisedException(errCode!, "请求方法被禁止"); + case 500: + return UnauthorisedException(errCode!, "服务器内部错误"); + case 502: + return UnauthorisedException(errCode!, "无效的请求"); + case 503: + return UnauthorisedException(errCode!, "服务器异常"); + case 505: + return UnauthorisedException(errCode!, "不支持HTTP协议请求"); + default: + return ApiException(errCode, error.response?.statusMessage ?? '未知错误'); + } + } on Exception catch (e) { + return ApiException(-1, unknownException); + } + default: + return ApiException(-1, error.message); + } + } + + factory ApiException.from(dynamic exception) { + if (exception is DioException) { + return ApiException.fromDioError(exception); + } + if (exception is ApiException) { + return exception; + } else { + var apiException = ApiException(-1, unknownException); + apiException.stackInfo = exception?.toString(); + return apiException; + } + } +} + +/// 请求错误 +class BadRequestException extends ApiException { + BadRequestException([int? code, String? message]) : super(code, message); +} + +/// 未认证异常 +class UnauthorisedException extends ApiException { + UnauthorisedException([int code = -1, String message = '']) : super(code, message); +} diff --git a/lib/common/request/exception_handler.dart b/lib/common/request/exception_handler.dart new file mode 100644 index 0000000..1997ed8 --- /dev/null +++ b/lib/common/request/exception_handler.dart @@ -0,0 +1,19 @@ +import 'package:flutter_easyloading/flutter_easyloading.dart'; + +import 'exception.dart'; + + +bool handleException(ApiException exception, + {bool Function(ApiException)? onError}) { + if (onError?.call(exception) == true) { + return true; + } + + if (exception.code == 401) { + ///todo to login + return true; + } + EasyLoading.showError(exception.message ?? ApiException.unknownException); + + return false; +} diff --git a/lib/common/request/request.dart b/lib/common/request/request.dart new file mode 100644 index 0000000..a40dfbe --- /dev/null +++ b/lib/common/request/request.dart @@ -0,0 +1,16 @@ +import '../../utils/loading.dart'; +import 'exception.dart'; +import 'exception_handler.dart'; + +Future request( + Function() block, { + bool showLoading = true, + bool Function(ApiException)? onError, +}) async { + try { + await loading(block, isShowLoading: showLoading); + } catch (e) { + handleException(ApiException.from(e), onError: onError); + } + return; +} diff --git a/lib/common/request/request_client.dart b/lib/common/request/request_client.dart new file mode 100644 index 0000000..801576f --- /dev/null +++ b/lib/common/request/request_client.dart @@ -0,0 +1,162 @@ +import 'dart:convert'; + +import 'package:dio/dio.dart'; +import 'package:pretty_dio_logger/pretty_dio_logger.dart'; + +import 'api_response/api_response_entity.dart'; +import 'api_response/raw_data.dart'; +import 'config.dart'; +import 'exception.dart'; +import 'token_interceptor.dart'; + +RequestClient requestClient = RequestClient(); + +class RequestClient { + late Dio _dio; + + RequestClient() { + _dio = Dio(BaseOptions(baseUrl: RequestConfig.baseUrl, connectTimeout: RequestConfig.connectTimeout)); + _dio.interceptors.add(TokenInterceptor()); + _dio.interceptors.add(PrettyDioLogger(requestHeader: true, requestBody: true, responseHeader: true)); + } + + Future request( + String url, { + required String method, + Map? queryParameters, + data, + Map? headers, + Function(ApiResponse)? onResponse, + bool Function(ApiException)? onError, + }) async { + try { + Options options = Options() + ..method = method + ..headers = headers; + + data = _convertRequestData(data); + + Response response = await _dio.request(url, queryParameters: queryParameters, data: data, options: options); + + return _handleResponse(response, onResponse); + } catch (e) { + var exception = ApiException.from(e); + if (onError?.call(exception) != true) { + throw exception; + } + } + + return null; + } + + _convertRequestData(data) { + if (data != null) { + data = jsonDecode(jsonEncode(data)); + } + return data; + } + + /// get + Future get( + String url, { + Map? queryParameters, + Map? headers, + bool showLoading = true, + Function(ApiResponse)? onResponse, + bool Function(ApiException)? onError, + }) { + return request(url, + method: 'GET', queryParameters: queryParameters, headers: headers, onResponse: onResponse, onError: onError); + } + + /// post + Future post( + String url, { + Map? queryParameters, + data, + Map? headers, + bool showLoading = true, + Function(ApiResponse)? onResponse, + bool Function(ApiException)? onError, + }) { + return request(url, + method: "POST", + queryParameters: queryParameters, + data: data, + headers: headers, + onResponse: onResponse, + onError: onError); + } + + /// delete + Future delete( + String url, { + Map? queryParameters, + data, + Map? headers, + bool showLoading = true, + Function(ApiResponse)? onResponse, + bool Function(ApiException)? onError, + }) { + return request(url, + method: "DELETE", + queryParameters: queryParameters, + data: data, + headers: headers, + onResponse: onResponse, + onError: onError); + } + + /// put + Future put( + String url, { + Map? queryParameters, + data, + Map? headers, + bool showLoading = true, + Function(ApiResponse)? onResponse, + bool Function(ApiException)? onError, + }) { + return request(url, + method: "PUT", + queryParameters: queryParameters, + data: data, + headers: headers, + onResponse: onResponse, + onError: onError); + } + + /// 请求响应内容处理 + T? _handleResponse( + Response response, + Function(ApiResponse)? onResponse, + ) { + int statusCode = response.statusCode ?? -1; + print('statusCode=$statusCode'); + // 200..299 成功响应 + if (statusCode >= 200 && statusCode <= 299) { + if (T.toString() == (RawData).toString()) { + RawData raw = RawData(); + raw.value = response.data; + return raw as T; + } else { + ApiResponse apiResponse = ApiResponse.fromJson(response.data); + onResponse?.call(apiResponse); + return _handleBusinessResponse(apiResponse); + } + } else { + var exception = ApiException(response.statusCode, ApiException.unknownException); + throw exception; + } + } + + /// 业务内容处理 + T? _handleBusinessResponse(ApiResponse response) { + if (response.code == RequestConfig.successCode) { + return response.data; + } else { + var exception = ApiException(response.code, response.msg); + throw exception; + } + } +} diff --git a/lib/common/request/token_interceptor.dart b/lib/common/request/token_interceptor.dart new file mode 100644 index 0000000..01ab281 --- /dev/null +++ b/lib/common/request/token_interceptor.dart @@ -0,0 +1,11 @@ +import 'package:dio/dio.dart'; + +class TokenInterceptor extends Interceptor { + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + /// todo 判断token不为空插入 + options.headers["Auth-token"] = ''; + options.headers["version"] = '1.0.0'; + super.onRequest(options, handler); + } +} diff --git a/lib/generated/json/base/json_convert_content.dart b/lib/generated/json/base/json_convert_content.dart new file mode 100644 index 0000000..26eff33 --- /dev/null +++ b/lib/generated/json/base/json_convert_content.dart @@ -0,0 +1,110 @@ +// ignore_for_file: non_constant_identifier_names +// ignore_for_file: camel_case_types +// ignore_for_file: prefer_single_quotes + +// This file is automatically generated. DO NOT EDIT, all your changes would be lost. +import 'package:flutter/material.dart' show debugPrint; +import 'package:wow_english/models/user_entity.dart'; + +JsonConvert jsonConvert = JsonConvert(); +typedef JsonConvertFunction = T Function(Map json); +typedef EnumConvertFunction = T Function(String value); + +class JsonConvert { + static final Map convertFuncMap = { + (UserEntity).toString(): UserEntity.fromJson, + }; + + T? convert(dynamic value, {EnumConvertFunction? enumConvert}) { + if (value == null) { + return null; + } + if (value is T) { + return value; + } + try { + return _asT(value, enumConvert: enumConvert); + } catch (e, stackTrace) { + debugPrint('asT<$T> $e $stackTrace'); + return null; + } + } + + List? convertList(List? value, {EnumConvertFunction? enumConvert}) { + if (value == null) { + return null; + } + try { + return value.map((dynamic e) => _asT(e,enumConvert: enumConvert)).toList(); + } catch (e, stackTrace) { + debugPrint('asT<$T> $e $stackTrace'); + return []; + } + } + +List? convertListNotNull(dynamic value, {EnumConvertFunction? enumConvert}) { + if (value == null) { + return null; + } + try { + return (value as List).map((dynamic e) => _asT(e,enumConvert: enumConvert)!).toList(); + } catch (e, stackTrace) { + debugPrint('asT<$T> $e $stackTrace'); + return []; + } + } + + T? _asT(dynamic value, + {EnumConvertFunction? enumConvert}) { + final String type = T.toString(); + final String valueS = value.toString(); + if (enumConvert != null) { + return enumConvert(valueS) as T; + } else if (type == "String") { + return valueS as T; + } else if (type == "int") { + final int? intValue = int.tryParse(valueS); + if (intValue == null) { + return double.tryParse(valueS)?.toInt() as T?; + } else { + return intValue as T; + } + } else if (type == "double") { + return double.parse(valueS) as T; + } else if (type == "DateTime") { + return DateTime.parse(valueS) as T; + } else if (type == "bool") { + if (valueS == '0' || valueS == '1') { + return (valueS == '1') as T; + } + return (valueS == 'true') as T; + } else if (type == "Map" || type.startsWith("Map<")) { + return value as T; + } else { + if (convertFuncMap.containsKey(type)) { + return convertFuncMap[type]!(Map.from(value)) as T; + } else { + throw UnimplementedError('$type unimplemented'); + } + } + } + + //list is returned by type + static M? _getListChildType(List> data) { + if([] is M){ + return data.map((Map e) => UserEntity.fromJson(e)).toList() as M; + } + + debugPrint("${M.toString()} not found"); + + return null; +} + + static M? fromJsonAsT(dynamic json) { + if (json is List) { + return _getListChildType(json.map((e) => e as Map).toList()); + } else { + return jsonConvert.convert(json); + } + } +} diff --git a/lib/generated/json/base/json_field.dart b/lib/generated/json/base/json_field.dart new file mode 100644 index 0000000..39f3c15 --- /dev/null +++ b/lib/generated/json/base/json_field.dart @@ -0,0 +1,25 @@ +// ignore_for_file: non_constant_identifier_names +// ignore_for_file: camel_case_types +// ignore_for_file: prefer_single_quotes + +// This file is automatically generated. DO NOT EDIT, all your changes would be lost. + +class JsonSerializable{ + const JsonSerializable(); +} + +class JSONField { + //Specify the parse field name + final String? name; + + //Whether to participate in toJson + final bool? serialize; + + //Whether to participate in fromMap + final bool? deserialize; + + //Enumeration or not + final bool? isEnum; + + const JSONField({this.name, this.serialize, this.deserialize, this.isEnum}); +} diff --git a/lib/generated/json/user_entity.g.dart b/lib/generated/json/user_entity.g.dart new file mode 100644 index 0000000..aee254e --- /dev/null +++ b/lib/generated/json/user_entity.g.dart @@ -0,0 +1,52 @@ +import 'package:wow_english/generated/json/base/json_convert_content.dart'; +import 'package:wow_english/models/user_entity.dart'; + +UserEntity $UserEntityFromJson(Map json) { + final UserEntity userEntity = UserEntity(); + final int? id = jsonConvert.convert(json['id']); + if (id != null) { + userEntity.id = id; + } + final String? name = jsonConvert.convert(json['name']); + if (name != null) { + userEntity.name = name; + } + final int? age = jsonConvert.convert(json['age']); + if (age != null) { + userEntity.age = age; + } + final int? gender = jsonConvert.convert(json['gender']); + if (gender != null) { + userEntity.gender = gender; + } + final String? avatarUrl = jsonConvert.convert(json['avatarUrl']); + if (avatarUrl != null) { + userEntity.avatarUrl = avatarUrl; + } + final String? phoneNum = jsonConvert.convert(json['phoneNum']); + if (phoneNum != null) { + userEntity.phoneNum = phoneNum; + } + final String? token = jsonConvert.convert(json['token']); + if (token != null) { + userEntity.token = token; + } + final int? expireTime = jsonConvert.convert(json['expireTime']); + if (expireTime != null) { + userEntity.expireTime = expireTime; + } + return userEntity; +} + +Map $UserEntityToJson(UserEntity entity) { + final Map data = {}; + data['id'] = entity.id; + data['name'] = entity.name; + data['age'] = entity.age; + data['gender'] = entity.gender; + data['avatarUrl'] = entity.avatarUrl; + data['phoneNum'] = entity.phoneNum; + data['token'] = entity.token; + data['expireTime'] = entity.expireTime; + return data; +} diff --git a/lib/home/home_page.dart b/lib/home/home_page.dart index 2ddb912..5ed4328 100644 --- a/lib/home/home_page.dart +++ b/lib/home/home_page.dart @@ -32,6 +32,8 @@ class _HomePageView extends StatelessWidget { Navigator.of(AppRouter.context).pushNamed(AppRouteName.listen); } else if (type == HeaderActionType.shop) { Navigator.of(AppRouter.context).pushNamed(AppRouteName.shop); + } else if (type == HeaderActionType.user) { + Navigator.of(AppRouter.context).pushNamed(AppRouteName.user); } else { // Navigator.of(AppRouter.context).pushNamed(AppRouteName.topicPic); // Navigator.of(AppRouter.context).pushNamed(AppRouteName.topicWord); @@ -117,4 +119,4 @@ class _HomePageView extends StatelessWidget { ), ); }); -} \ No newline at end of file +} diff --git a/lib/home/widgets/home_tab_header_widget.dart b/lib/home/widgets/home_tab_header_widget.dart index c90ba8a..53eaedf 100644 --- a/lib/home/widgets/home_tab_header_widget.dart +++ b/lib/home/widgets/home_tab_header_widget.dart @@ -12,7 +12,7 @@ enum HeaderActionType { //购买 shop, //个人信息 - userinfo + user } class HomeTabHeaderWidget extends StatelessWidget { @@ -34,7 +34,7 @@ class HomeTabHeaderWidget extends StatelessWidget { GestureDetector( onTap: () { if(actionTap != null) { - actionTap!(HeaderActionType.userinfo); + actionTap!(HeaderActionType.user); } }, child: Container( @@ -56,7 +56,7 @@ class HomeTabHeaderWidget extends StatelessWidget { GestureDetector( onTap: () { if(actionTap != null) { - actionTap!(HeaderActionType.userinfo); + actionTap!(HeaderActionType.user); } }, child: Container( @@ -128,4 +128,4 @@ class HomeTabHeaderWidget extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/login/loginpage/bloc/login_bloc.dart b/lib/login/loginpage/bloc/login_bloc.dart index be0c5be..6f9fa63 100644 --- a/lib/login/loginpage/bloc/login_bloc.dart +++ b/lib/login/loginpage/bloc/login_bloc.dart @@ -1,7 +1,12 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:wow_english/network/api.dart'; -import 'package:wow_english/network/network_manager.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; + +import '../../../common/request/api_response/api_response_entity.dart'; +import '../../../common/request/apis.dart'; +import '../../../common/request/request.dart'; +import '../../../common/request/request_client.dart'; +import '../../../models/user_entity.dart'; part 'login_event.dart'; part 'login_state.dart'; @@ -9,26 +14,35 @@ part 'login_state.dart'; enum LoginType { ///密码登陆 pwd, + ///验证码登陆 sms, } class LoginBloc extends Bloc { - bool _canLogin = false; + ///是否可以发送验证码 bool _canSendSms = false; + ///是否阅读协议 bool _agreement = false; + ///登陆方式 - LoginType _loginType = LoginType.sms; + //LoginType _loginType = LoginType.sms; + bool _isSmsLoginType = true; final TextEditingController phoneNumController = TextEditingController(); final TextEditingController checkNumController = TextEditingController(); bool get canLogin => _canLogin; + bool get agreement => _agreement; - LoginType get loginType => _loginType; + + //LoginType get loginType => _loginType; + + bool get isSmsLoginType => _isSmsLoginType; + bool get canSendSms => _canSendSms; LoginBloc() : super(LoginInitial()) { @@ -40,22 +54,40 @@ class LoginBloc extends Bloc { on(_requestSmsCodeApi); } - ///请求登陆 void _requestLoginApi(RequestLoginEvent event, Emitter emitter) async { - await DioUtil().requestData( - Api.login, - method: HttpMethod.post, - data: { - 'phoneNum':'17730280759', - 'type':_loginType.toString(), - 'password':'asd123456'}, - successCallBack: (data){ - emitter(LoginResultChangeState(true)); + var phoneNumber = phoneNumController.text; + if (phoneNumber.isEmpty) { + EasyLoading.showToast('号码不能为空'); + return; + } + + var checkNumber = checkNumController.text; + if (checkNumber.isEmpty) { + var text = _isSmsLoginType ? '密码' : '验证码'; + EasyLoading.showToast('$text不能为空'); + return; + } + var checkKey = _isSmsLoginType ? 'smsCode' : 'password'; + var type = _isSmsLoginType ? 'sms_code' : 'pwd'; + + request(() async { + var params = {'phoneNum': phoneNumber, 'type': type, checkKey: checkNumber}; + await requestClient.post( + Apis.login, + data: params, + onResponse: (ApiResponse response) { + print('response=$response'); + // todo 持久化用户对象 + // todo 写入全局对象 + //emitter.call(LoginResultChangeState()); + }, + onError: (e) { + EasyLoading.showToast('登陆失败:${e.message}'); + return true; }, - errorCallBack: (error){ - emitter(LoginResultChangeState(false)); - }); + ); + }); } ///请求验证码 @@ -65,10 +97,10 @@ class LoginBloc extends Bloc { ///切换登陆方式 void _changeLoginType(ChangeLoginTypeEvent event, Emitter emitter) async { - if (_loginType == LoginType.sms) { - _loginType = LoginType.pwd; + if (_isSmsLoginType) { + _isSmsLoginType = false; } else { - _loginType = LoginType.sms; + _isSmsLoginType = true; } checkNumController.clear(); if (_loginStateChange()) { @@ -79,7 +111,7 @@ class LoginBloc extends Bloc { ///手机号输入 void _changePhoneNumber(PhoneNumChangeEvent event, Emitter emitter) async { - if(phoneNumController.text.isNotEmpty) { + if (phoneNumController.text.isNotEmpty) { if (!_canSendSms) { _canSendSms = true; emitter(SmsSendTypeChangeState()); @@ -99,7 +131,7 @@ class LoginBloc extends Bloc { } ///验证码/密码输入变化 - void _checkFiledChange(CheckFieldChangeEvent event,Emitter emitter) async { + void _checkFiledChange(CheckFieldChangeEvent event, Emitter emitter) async { if (_loginStateChange()) { emitter(LoginEventChangeState()); } diff --git a/lib/login/loginpage/bloc/login_state.dart b/lib/login/loginpage/bloc/login_state.dart index 360d7c5..191c310 100644 --- a/lib/login/loginpage/bloc/login_state.dart +++ b/lib/login/loginpage/bloc/login_state.dart @@ -4,18 +4,20 @@ part of 'login_bloc.dart'; abstract class LoginState {} class LoginInitial extends LoginState {} + ///登陆按钮状态 class LoginEventChangeState extends LoginState {} + ///切换登陆方式 class LoginTypeChangeState extends LoginState {} + ///发送验证码按钮状态 class SmsSendTypeChangeState extends LoginState {} + ///是否同意协议 class AgreementTypeChangeState extends LoginState {} ///获取验证码 class SmsCodeRequestState extends LoginState {} + ///登陆请求结果 -class LoginResultChangeState extends LoginState { - bool result = false; - LoginResultChangeState(this.result); -} +class LoginResultChangeState extends LoginState {} diff --git a/lib/login/loginpage/login_page.dart b/lib/login/loginpage/login_page.dart index 961e693..2b59b64 100644 --- a/lib/login/loginpage/login_page.dart +++ b/lib/login/loginpage/login_page.dart @@ -1,7 +1,6 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/common/widgets/textfield_customer_widget.dart'; @@ -27,7 +26,6 @@ class _LoginPageView extends StatelessWidget { return BlocListener( listener: (context, state){ if (state is LoginResultChangeState) { - EasyLoading.showToast('登陆接口回调'); Navigator.of(context).pushNamed(AppRouteName.home); } }, @@ -61,7 +59,7 @@ class _LoginPageView extends StatelessWidget { ), padding: EdgeInsets.symmetric(horizontal: 18.w,vertical: 5.h), child: Text( - bloc.loginType == LoginType.sms?'密码登陆':'验证码密码', + bloc.isSmsLoginType?'密码登陆':'验证码密码', style: TextStyle( fontSize: 16.sp ), @@ -78,11 +76,11 @@ class _LoginPageView extends StatelessWidget { width: 131.w, ), Offstage( - offstage: bloc.loginType == LoginType.pwd, + offstage: !bloc.isSmsLoginType, child: _buildSmsViewWidget(), ), Offstage( - offstage: bloc.loginType == LoginType.sms, + offstage: bloc.isSmsLoginType, child: _buildPwdViewWidget(), ), Row( @@ -313,5 +311,3 @@ class _LoginPageView extends StatelessWidget { ); }); } - - diff --git a/lib/models/response_model.dart b/lib/models/response_model.dart.txt index 3f891db..3f891db 100644 --- a/lib/models/response_model.dart +++ b/lib/models/response_model.dart.txt diff --git a/lib/models/response_model.g.dart b/lib/models/response_model.g.dart.txt index 81a714a..f7e30ec 100644 --- a/lib/models/response_model.g.dart +++ b/lib/models/response_model.g.dart.txt @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'response_model.dart'; +part of 'response_model.dart.txt'; // ************************************************************************** // JsonSerializableGenerator diff --git a/lib/models/test_model.dart b/lib/models/test_model.dart deleted file mode 100644 index 9f6d4a0..0000000 --- a/lib/models/test_model.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; - -part 'test_model.g.dart'; - -@JsonSerializable() -class TestModel { - @JsonKey(name: 'test_name') - String? name; - @JsonKey(defaultValue: '男') - String? sex; - int? age; - - TestModel({this.name,this.sex,this.age}); - - factory TestModel.fromJson(Map json) => _$TestModelFromJson(json); - - Map toJson() => _$TestModelToJson(this); -} \ No newline at end of file diff --git a/lib/models/test_model.g.dart b/lib/models/test_model.g.dart deleted file mode 100644 index 2c80e8c..0000000 --- a/lib/models/test_model.g.dart +++ /dev/null @@ -1,19 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'test_model.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -TestModel _$TestModelFromJson(Map json) => TestModel( - name: json['test_name'] as String?, - sex: json['sex'] as String? ?? '男', - age: json['age'] as int?, - ); - -Map _$TestModelToJson(TestModel instance) => { - 'test_name': instance.name, - 'sex': instance.sex, - 'age': instance.age, - }; diff --git a/lib/models/user_entity.dart b/lib/models/user_entity.dart new file mode 100644 index 0000000..6efa7bd --- /dev/null +++ b/lib/models/user_entity.dart @@ -0,0 +1,27 @@ +import 'dart:convert'; + +import 'package:wow_english/generated/json/base/json_field.dart'; +import 'package:wow_english/generated/json/user_entity.g.dart'; + +@JsonSerializable() +class UserEntity { + late int id; + late String name; + late int age; + late int gender; + late String avatarUrl; + late String phoneNum; + late String token; + late int expireTime; + + UserEntity(); + + factory UserEntity.fromJson(Map json) => $UserEntityFromJson(json); + + Map toJson() => $UserEntityToJson(this); + + @override + String toString() { + return jsonEncode(this); + } +} diff --git a/lib/network/api.dart b/lib/network/api.dart.txt index 65f2840..65f2840 100644 --- a/lib/network/api.dart +++ b/lib/network/api.dart.txt diff --git a/lib/network/basic_configuration.dart b/lib/network/basic_configuration.dart.txt index 435427d..c4c60cb 100644 --- a/lib/network/basic_configuration.dart +++ b/lib/network/basic_configuration.dart.txt @@ -1,6 +1,7 @@ enum DevelopEvent { ///开发环境 dev, + ///正式环境 formal } @@ -12,12 +13,13 @@ class BasicConfigurationManager { //服务器地址 String? baseUrl; + //SessionId String? sessionId; - BasicConfigurationManager._internal(){ + BasicConfigurationManager._internal() { DevelopEvent developType = DevelopEvent.dev; - if(developType == DevelopEvent.dev) { + if (developType == DevelopEvent.dev) { baseUrl = 'http://wow-app.dev.kouyuxingqiu.com/'; } else { baseUrl = 'http://wow-app.dev.kouyuxingqiu.com/'; diff --git a/lib/network/network_manager.dart b/lib/network/network_manager.dart.txt index c34fc78..9522223 100644 --- a/lib/network/network_manager.dart +++ b/lib/network/network_manager.dart.txt @@ -1,11 +1,7 @@ -import 'dart:io'; - import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:wow_english/models/response_model.dart'; -import 'package:wow_english/network/basic_configuration.dart'; - +import 'package:wow_english/network/basic_configuration.dart.txt'; enum HttpMethod { get, @@ -27,24 +23,22 @@ class DioUtil { static BaseOptions getDefOptions() { final BaseOptions options = BaseOptions(); - options.baseUrl = BasicConfigurationManager().baseUrl??''; + options.baseUrl = BasicConfigurationManager().baseUrl ?? ''; options.connectTimeout = const Duration(milliseconds: 1000); options.receiveTimeout = const Duration(milliseconds: 1000); - options.headers['content-type'] = 'application/x-www-form-urlencoded'; return options; } Future requestData( - String path, { - data, - // HttpMethod method = HttpMethod.post, - Map? queryParameters, - ProgressCallback? onSendProgress, - ProgressCallback? onReceiveProgress, - required Function successCallBack, - required Function errorCallBack, - required HttpMethod method, - }) async{ + HttpMethod method, + String path, { + data, + Function? successCallBack, + Function? errorCallBack, + Map? queryParameters, + ProgressCallback? onSendProgress, + ProgressCallback? onReceiveProgress, + }) async { try { Map headers = {}; @@ -52,32 +46,31 @@ class DioUtil { headers['content-type'] = 'application/json'; } Response response; - response = await _dio.request( - path, - data: data??{}, + response = await _dio.request(path, + data: data ?? {}, queryParameters: queryParameters, - options: Options(method: method.name,headers: headers), + options: Options(method: method.name, headers: headers), onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress); - if (response.statusCode == HttpStatus.ok || response.statusCode == HttpStatus.created) { + int statusCode = response.statusCode ?? 0; + if (statusCode >= 200 && statusCode < 300) { if (kDebugMode) { print(response.data); } final ResponseModel model = ResponseModel.fromJson(response.data); - if (model.code != 200) { - errorCallBack(model.msg); + if (model.code == 200) { + successCallBack?.call(response.data); } else { - successCallBack(response.data); + errorCallBack?.call(model.msg); } } else { - errorCallBack('请求失败'); + errorCallBack?.call('请求失败'); } - } on DioError catch(error) { - EasyLoading.dismiss(); + } on DioException catch (error) { if (kDebugMode) { print(error); } - rethrow; + errorCallBack?.call("网络错误: ${error.message}"); } } -} \ No newline at end of file +} diff --git a/lib/utils/loading.dart b/lib/utils/loading.dart new file mode 100644 index 0000000..f63e6aa --- /dev/null +++ b/lib/utils/loading.dart @@ -0,0 +1,23 @@ +import 'package:flutter_easyloading/flutter_easyloading.dart'; + +Future loading(Function block, {bool isShowLoading = true}) async { + if (isShowLoading) { + showLoading(); + } + try { + await block(); + } catch (e) { + rethrow; + } finally { + dismissLoading(); + } + return; +} + +void showLoading() { + EasyLoading.show(status: "加载中..."); +} + +void dismissLoading() { + EasyLoading.dismiss(); +} diff --git a/pubspec.yaml b/pubspec.yaml index c87d625..1185e10 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,8 @@ dependencies: cupertino_icons: ^1.0.2 #网络请求 https://pub.dev/packages/dio dio: ^5.1.2 + # https://pub.dev/packages/pretty_dio_logger + pretty_dio_logger: ^1.3.1 #谷歌字体 https://pub.dev/packages/google_fonts google_fonts: ^4.0.4 #状态管理 https://pub.dev/packages/flutter_bloc @@ -64,9 +66,9 @@ dependencies: # 拍照,从相册中选择 https://pub.flutter-io.cn/packages/image_picker image_picker: ^0.8.7+5 # 支付宝支付SDK https://pub.flutter-io.cn/packages/tobias -# tobias: ^3.1.0 + # tobias: ^3.1.0 # 微信SDK相关 https://pub.flutter-io.cn/packages/fluwx -# fluwx: ^4.2.4+1 + # fluwx: ^4.2.4+1 # json数据解析 https://pub.flutter-io.cn/packages/json_annotation json_annotation: ^4.8.1 # double丢失精度问题 https://pub.dev/packages/decimal