diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5c37c7f..dce4021 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,29 +1,55 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/api/api_path.dart b/lib/api/api_path.dart new file mode 100644 index 0000000..3b602a3 --- /dev/null +++ b/lib/api/api_path.dart @@ -0,0 +1,3 @@ +class ApiPath { + static const String login = "/auth/login"; +} diff --git a/lib/api/api_response.dart b/lib/api/api_response.dart new file mode 100644 index 0000000..cd85dfe --- /dev/null +++ b/lib/api/api_response.dart @@ -0,0 +1,51 @@ +class ApiResponse { + final bool success; + final T? data; + final String? message; + final int? statusCode; + + // 构造函数 + const ApiResponse({ + required this.success, + this.data, + this.message, + this.statusCode, + }); + + // 成功响应 + factory ApiResponse.success(T data, + {String message = 'Success', int? statusCode}) { + return ApiResponse( + success: true, + data: data, + message: message, + statusCode: statusCode, + ); + } + + // 失败响应 + factory ApiResponse.error(String message, {int? statusCode, T? data}) { + return ApiResponse( + success: false, + message: message, + statusCode: statusCode, + data: data, // 可选:返回部分数据(如错误时的缓存数据) + ); + } + + // 加载中(可选) + factory ApiResponse.loading() { + return ApiResponse( + success: false, + message: 'Loading...', + ); + } + + @override + List get props => [success, data, message, statusCode]; + + @override + String toString() { + return 'ApiResponse(success: $success, message: $message, data: $data, statusCode: $statusCode)'; + } +} diff --git a/lib/api/base_api_service.dart b/lib/api/base_api_service.dart new file mode 100644 index 0000000..8cdc46d --- /dev/null +++ b/lib/api/base_api_service.dart @@ -0,0 +1,85 @@ +// api/service/user_api_service.dart +import 'package:dio/dio.dart' as dioAlias; +import 'package:starwork_flutter/api/api_response.dart'; +import 'package:starwork_flutter/flavors.dart'; + +class BaseApiService { + final dioAlias.Dio dio = dioAlias.Dio(); + + BaseApiService() { + dio.options.baseUrl = F.apiHost; + dio.options.connectTimeout = const Duration(seconds: 30); + dio.options.receiveTimeout = const Duration(seconds: 30); + // 可添加拦截器、token 等 + } + + /// 统一请求方法(私有,但子类可用) + Future> makeRequest({ + required String path, + String method = 'GET', + dynamic data, + Map? queryParameters, + required T Function(dynamic) fromJson, + }) async { + try { + dioAlias.Response response; + + switch (method.toUpperCase()) { + case 'GET': + response = await dio.get(path, queryParameters: queryParameters); + break; + case 'POST': + response = await dio.post(path, + data: data, queryParameters: queryParameters); + break; + case 'PUT': + response = + await dio.put(path, data: data, queryParameters: queryParameters); + break; + case 'DELETE': + response = await dio.delete(path, + data: data, queryParameters: queryParameters); + break; + case 'PATCH': + response = await dio.patch(path, + data: data, queryParameters: queryParameters); + break; + default: + return ApiResponse.error('Unsupported method: $method'); + } + + if (response.statusCode! >= 200 && response.statusCode! < 300) { + final parsedData = fromJson(response.data); + return ApiResponse.success( + parsedData, + statusCode: response.statusCode, + message: 'Success', + ); + } else { + return ApiResponse.error( + response.statusMessage ?? 'Request failed', + statusCode: response.statusCode, + ); + } + } on dioAlias.DioException catch (e) { + String message = 'Unknown error'; + int? statusCode = e.response?.statusCode; + + if (e.type == dioAlias.DioExceptionType.connectionTimeout) { + message = 'Connection timeout'; + } else if (e.type == dioAlias.DioExceptionType.receiveTimeout) { + message = 'Server timeout'; + } else if (e.type == dioAlias.DioExceptionType.badResponse) { + message = e.response?.statusMessage ?? 'Bad response'; + } else if (e.type == dioAlias.DioExceptionType.connectionError) { + message = 'Network not available'; + } else { + message = e.message ?? 'Something went wrong'; + } + + return ApiResponse.error(message, statusCode: statusCode); + } catch (e) { + return ApiResponse.error('Unexpected error: $e'); + } + } +} diff --git a/lib/api/model/user/user_model.dart b/lib/api/model/user/user_model.dart new file mode 100644 index 0000000..ef0109d --- /dev/null +++ b/lib/api/model/user/user_model.dart @@ -0,0 +1,24 @@ + +class UserModel { + final int id; + final String name; + final String email; + + UserModel({required this.id, required this.name, required this.email}); + + factory UserModel.fromJson(Map json) { + return UserModel( + id: json['id'], + name: json['name'], + email: json['email'], + ); + } + + Map toJson() { + return { + 'id': id, + 'name': name, + 'email': email, + }; + } +} \ No newline at end of file diff --git a/lib/api/service/user_api_service.dart b/lib/api/service/user_api_service.dart new file mode 100644 index 0000000..e3491e0 --- /dev/null +++ b/lib/api/service/user_api_service.dart @@ -0,0 +1,18 @@ +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/api_response.dart'; +import 'package:starwork_flutter/api/base_api_service.dart'; +import 'package:starwork_flutter/api/model/user/user_model.dart'; + +class UserApiService { + final BaseApiService _api = BaseApiService(); + + Future> login(Map userData) { + return _api.makeRequest( + // 通过实例调用 + path: '/login', + method: 'POST', + data: userData, + fromJson: (data) => UserModel.fromJson(data), + ); + } +} diff --git a/lib/app.dart b/lib/app.dart index 7302a7f..0a57d6e 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -42,7 +42,7 @@ class App extends StatelessWidget { fallbackLocale: const Locale('zh', 'CN'), // 必须设置 fallback 没有该语言时使用 getPages: AppPages.pages, - initialRoute: AppRoutes.login, + initialRoute: AppRoutes.main, debugShowCheckedModeBanner: false, ); }, diff --git a/lib/base/app_initialization.dart b/lib/base/app_initialization.dart index ac9c77c..684e53f 100644 --- a/lib/base/app_initialization.dart +++ b/lib/base/app_initialization.dart @@ -4,6 +4,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:starwork_flutter/api/base_api_service.dart'; +import 'package:starwork_flutter/api/service/user_api_service.dart'; import 'package:starwork_flutter/common/utils/shared_preferences_utils.dart'; import 'package:starwork_flutter/i18n/app_i18n.dart'; @@ -15,6 +17,8 @@ class AppInitialization { setSystemStatusBar(); // 初始化 SharedPreferences await SharedPreferencesUtils.init(); + // 初始化api服务(单例) + Get.put(BaseApiService(),permanent: true); } static void setSystemStatusBar() { diff --git a/lib/base/app_permission.dart b/lib/base/app_permission.dart new file mode 100644 index 0000000..57a990c --- /dev/null +++ b/lib/base/app_permission.dart @@ -0,0 +1,23 @@ +import 'package:permission_handler/permission_handler.dart'; + +class AppPermission { + // 检查权限 + static Future checkPermission({required Permission permission}) async { + var status = await permission.status; + return status == PermissionStatus.granted; + } + + // 批量检查权限 + static Future checkPermissions({ + required List permissions, + }) async { + if (permissions.isEmpty) return false; + Map statuses = await permissions.request(); + for (var status in statuses.values) { + if (status != PermissionStatus.granted) { + return false; + } + } + return true; + } +} diff --git a/lib/views/home/home_controller.dart b/lib/views/home/home_controller.dart index 8497904..a7f7638 100644 --- a/lib/views/home/home_controller.dart +++ b/lib/views/home/home_controller.dart @@ -1,5 +1,16 @@ +import 'package:get/get.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:starwork_flutter/base/app_permission.dart'; import 'package:starwork_flutter/base/base_controller.dart'; class HomeController extends BaseController { + final isOpenNotificationPermission = false.obs; + @override + void onInit() async { + super.onInit(); + isOpenNotificationPermission.value = await AppPermission.checkPermission( + permission: Permission.notification, + ); + } } diff --git a/lib/views/home/home_view.dart b/lib/views/home/home_view.dart index 9172e53..c43d8cb 100644 --- a/lib/views/home/home_view.dart +++ b/lib/views/home/home_view.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:starwork_flutter/base/app_permission.dart'; import 'home_controller.dart'; @@ -9,20 +12,76 @@ class HomeView extends GetView { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('HomeView'), - centerTitle: true, + body: SafeArea( + child: Container( + width: 1.sw, + padding: EdgeInsets.symmetric(horizontal: 15.w), + child: Column( + children: [ + _buildPageHead(), + _buildSystemNotificationPermissionRow(), + ], + ), + ), ), - body: Center( - child: ElevatedButton( - onPressed: () { - if (Get.locale?.languageCode == 'zh') { - Get.updateLocale(const Locale('en', 'US')); - } else { - Get.updateLocale(const Locale('zh', 'CN')); - } - }, - child: Text('切换成英语${'路由'.tr}')), + ); + } + + _buildPageHead() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '19104656的互联', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w500, + ), + ), + Icon( + Icons.arrow_right_rounded, + size: 24.sp, + ) + ], + ), + Icon( + Icons.add_circle_outline_rounded, + size: 24.sp, + ), + ], + ); + } + + _buildSystemNotificationPermissionRow() { + return Visibility( + visible: !controller.isOpenNotificationPermission.value, + child: Container( + decoration: const BoxDecoration( + color: Color(0xFFfdefdf), + ), + child: Row( + children: [ + Text( + '系统通知未开启,报警消息无法通知'.tr, + style: TextStyle( + color: Color(0xFFea8720), + ), + ), + Container( + child: Text( + '去开启'.tr, + style: TextStyle( + color: Colors.white, + ), + ), + ) + ], + ), ), ); } diff --git a/lib/views/main/main_binding.dart b/lib/views/main/main_binding.dart index 1650a95..033ff21 100644 --- a/lib/views/main/main_binding.dart +++ b/lib/views/main/main_binding.dart @@ -1,10 +1,16 @@ import 'package:get/get.dart'; +import 'package:starwork_flutter/views/home/home_controller.dart'; import 'package:starwork_flutter/views/login/login_controller.dart'; import 'package:starwork_flutter/views/main/main_controller.dart'; +import 'package:starwork_flutter/views/messages/messages_controller.dart'; +import 'package:starwork_flutter/views/mine/mine_controller.dart'; class MainBinding extends Bindings { @override void dependencies() { Get.lazyPut(() => MainController()); + Get.lazyPut(() => HomeController()); + Get.lazyPut(() => MessagesController()); + Get.lazyPut(() => MineController()); } } diff --git a/pubspec.lock b/pubspec.lock index 37a53dc..4b14e2b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -81,6 +81,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.8" + dio: + dependency: "direct main" + description: + name: dio + sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9 + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.9.0" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.1" fake_async: dependency: transitive description: @@ -165,6 +181,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.7.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.2" image: dependency: transitive description: @@ -253,6 +277,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.11.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.0" path: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e497416..8994dde 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,6 +29,8 @@ dependencies: fluttertoast: ^8.2.8 # 验证码输入框 pinput: ^5.0.1 + # 网络请求库 + dio: ^5.9.0 dev_dependencies: