diff --git a/.fvm/flutter_sdk b/.fvm/flutter_sdk new file mode 120000 index 0000000..e323414 --- /dev/null +++ b/.fvm/flutter_sdk @@ -0,0 +1 @@ +/Users/zhangmeng/fvm/versions/3.0.0 \ No newline at end of file diff --git a/.fvm/fvm_config.json b/.fvm/fvm_config.json new file mode 100644 index 0000000..d4e14b2 --- /dev/null +++ b/.fvm/fvm_config.json @@ -0,0 +1,4 @@ +{ + "flutterSdkVersion": "3.0.0", + "flavors": {} +} \ No newline at end of file diff --git a/lib/constants/api/api.dart b/lib/constants/api/api.dart new file mode 100644 index 0000000..a86bac4 --- /dev/null +++ b/lib/constants/api/api.dart @@ -0,0 +1,40 @@ +import '../environment/environment.dart'; + + +/// * user 用户接口 +/// * data 基础数据接口 +/// * product 产品接口 +class API { + static const tokenKey = 'Broker-Token'; + + ///HOST + static String get host { + if (DevEV.instance.dev) { + return 'https://apiwenche.oa00.com'; + } else { + return 'https://api.yunyunwenche.com'; + } + } + + static String get imageHost { + if (DevEV.instance.dev) { + return 'https://static.oa00.com/wenche'; + } else { + return 'https://static.yunyunwenche.com'; + } + } + + static String get baseURL => '$host/app/broker'; + //根分类 + ///文件 + static _File file = _File(); + +} + +class _File { + ///图片上传 + String get uploadImage => '/file/upload/image'; + + ///文件上传 + String get uploadFile => '/file/upload/file'; +} \ No newline at end of file diff --git a/lib/constants/api/app_theme.dart b/lib/constants/api/app_theme.dart new file mode 100644 index 0000000..616e69f --- /dev/null +++ b/lib/constants/api/app_theme.dart @@ -0,0 +1,193 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class AppTheme { + static ThemeData get theme { + return ThemeData(primarySwatch: Colors.blue).copyWith( + primaryColor: const Color(0xFF027AFF), + extensions: >[ + MyAppStyle( + mainColor: Colors.blue, + bodyText3: TextStyle( + fontSize: 30.sp, + color: const Color(0xFF333333), + ), + ) + ], + textTheme: ThemeData.light().textTheme.copyWith( + headline3: TextStyle( + fontSize: 40.sp, + color: const Color(0xFF333333), + fontWeight: FontWeight.bold, + ), + headline4: TextStyle( + fontSize: 36.sp, + color: const Color(0xFF111111), + fontWeight: FontWeight.bold, + ), + subtitle1: TextStyle( + fontSize: 32.sp, + color: const Color(0xFF333333), + ), + subtitle2: TextStyle( + fontSize: 28.sp, + color: const Color(0xFF333333), + ), + bodyText1: TextStyle( + fontSize: 24.sp, + color: const Color(0xFF333333), + ), + bodyText2: TextStyle( + fontSize: 28.sp, + color: const Color(0xFF333333), + ), + ), + floatingActionButtonTheme: const FloatingActionButtonThemeData().copyWith( + backgroundColor: const Color(0xFFFFD000), + ), + appBarTheme: AppBarTheme( + elevation: 0, + centerTitle: true, + iconTheme: const IconThemeData( + color: Color(0xFF333333), + ), + systemOverlayStyle: SystemUiOverlayStyle.dark, + toolbarTextStyle: TextTheme( + headline6: TextStyle( + color: const Color(0xFF333333), + fontSize: 36.sp, + fontWeight: FontWeight.bold, + ), + ).bodyText2, + titleTextStyle: TextTheme( + headline6: TextStyle( + color: const Color(0xFF333333), + fontSize: 36.sp, + fontWeight: FontWeight.bold, + ), + ).headline6, + ), + tabBarTheme: TabBarTheme( + labelColor: const Color(0xFF333333), + labelStyle: TextStyle( + fontSize: 28.sp, + fontWeight: FontWeight.w600, + ), + unselectedLabelStyle: TextStyle( + fontSize: 28.sp, + ), + indicatorSize: TabBarIndicatorSize.label, + ), + bottomNavigationBarTheme: const BottomNavigationBarThemeData( + selectedItemColor: Color(0xFF333333), + selectedLabelStyle: TextStyle( + fontWeight: FontWeight.bold, + ), + type: BottomNavigationBarType.fixed, + unselectedLabelStyle: TextStyle(), + ), + radioTheme: RadioThemeData( + fillColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.selected)) { + return const Color(0xFFFFD000); + } + return null; + }), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return const Color(0xFFFFF4D7); + } + return const Color(0xFFFFD000); + }), + elevation: MaterialStateProperty.all(0), + foregroundColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return const Color(0xFF666666); + } + return const Color(0xFF333333); + }), + textStyle: MaterialStateProperty.all(TextStyle( + fontSize: 32.sp, + fontWeight: FontWeight.bold, + )), + padding: MaterialStateProperty.all( + EdgeInsets.symmetric(horizontal: 76.w, vertical: 22.w), + ), + enableFeedback: true, + ), + ), + textButtonTheme: TextButtonThemeData( + style: ButtonStyle( + overlayColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return const Color(0xFFFFF4D7); + } + return const Color(0xFFFFD000).withOpacity(0.2); + }), + foregroundColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return const Color(0xFF666666); + } + return const Color(0xFF333333); + }), + ), + ), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + dividerColor: const Color(0xFFE8E8E8), + colorScheme: ColorScheme.fromSwatch() + .copyWith(secondary: const Color(0xFF027AFF)) + .copyWith(secondary: const Color(0xFF027AFF)), + ); + } +} + +class SystemStyle { + static const initial = SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.light, + systemNavigationBarColor: Colors.white, + ); + + static const yellowBottomBar = SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.light, + systemNavigationBarColor: Color(0xFFFFD000), + ); + + static genStyle({required Color bottom}) { + return SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.light, + systemNavigationBarColor: bottom, + ); + } +} + +@immutable +class MyAppStyle extends ThemeExtension { + final TextStyle? bodyText3; + final Color? mainColor; + @override + MyAppStyle copyWith({Color? mainColor, TextStyle? bodyText3}) { + return MyAppStyle( + mainColor: mainColor ?? this.mainColor, + bodyText3: bodyText3 ?? this.bodyText3); + } + + @override + ThemeExtension lerp(ThemeExtension? other, double t) { + if (other is! MyAppStyle) { + return this; + } + return MyAppStyle( + mainColor: Color.lerp(mainColor, other.mainColor, t), + bodyText3: TextStyle.lerp(bodyText3, other.bodyText3, t), + ); + } + + const MyAppStyle({ + this.bodyText3, + this.mainColor, + }); +} diff --git a/lib/constants/environment/environment.dart b/lib/constants/environment/environment.dart new file mode 100644 index 0000000..c1af185 --- /dev/null +++ b/lib/constants/environment/environment.dart @@ -0,0 +1,14 @@ + +class DevEV { + static final DevEV _instance = DevEV._(); + + DevEV._(); + + static DevEV get instance => _instance; + + bool dev = false; + + void setEnvironment(context, {required bool environment}) { + dev = environment; + } +} diff --git a/lib/main.dart b/lib/main.dart index 202509b..c443b60 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,41 @@ +import 'package:bot_toast/bot_toast.dart'; +import 'package:cloud_car_internal/providers/user_provider.dart'; +import 'package:cloud_car_internal/utils/api_client.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_ume/flutter_ume.dart'; // UME 框架 +import 'package:flutter_ume_kit_ui/flutter_ume_kit_ui.dart'; // UI 插件包 +import 'package:flutter_ume_kit_perf/flutter_ume_kit_perf.dart'; // 性能插件包 +import 'package:flutter_ume_kit_show_code/flutter_ume_kit_show_code.dart'; // 代码查看插件包 +import 'package:flutter_ume_kit_device/flutter_ume_kit_device.dart'; // 设备信息插件包 +import 'package:flutter_ume_kit_console/flutter_ume_kit_console.dart'; // debugPrint 插件包 +import 'package:flutter_ume_kit_dio/flutter_ume_kit_dio.dart'; +import 'package:get/get_navigation/src/root/get_material_app.dart'; +import 'package:provider/provider.dart'; +import 'constants/api/app_theme.dart'; // Dio 网络请求调试工具 void main() { - runApp(const MyApp()); + if (kDebugMode) { + PluginManager.instance // 注册插件 + ..register(WidgetInfoInspector()) + ..register(WidgetDetailInspector()) + ..register(ColorSucker()) + ..register(AlignRuler()) + ..register(ColorPicker()) // 新插件 + ..register(TouchIndicator()) // 新插件 + ..register(Performance()) + ..register(ShowCode()) + ..register(MemoryInfoPage()) + ..register(CpuInfoPage()) + ..register(DeviceInfoPanel()) + ..register(Console()) + ..register(DioInspector(dio: apiClient.dio)); + runApp(UMEWidget(child: MyApp(), enable: true)); // 初始 + } else { + runApp(MyApp()); + } } class MyApp extends StatelessWidget { @@ -10,21 +44,43 @@ class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. - primarySwatch: Colors.blue, + return MultiProvider( + providers: [ + ChangeNotifierProvider(create: (context) => UserProvider()), + ], + child: ScreenUtilInit( + designSize: const Size(750, 1334), + builder: (context) => GestureDetector( + onTap: () { + //点击输入框外部隐藏键盘⌨️ + //只能响应点击非手势识别的组件 + FocusScopeNode currentFocus = FocusScope.of(context); + if (!currentFocus.hasPrimaryFocus && + currentFocus.focusedChild != null) { + FocusManager.instance.primaryFocus!.unfocus(); + } + }, + child: GetMaterialApp( + onGenerateTitle: (context) => '云云问车', + debugShowCheckedModeBanner: false, + theme: AppTheme.theme, + home: MyHomePage(title: ''), + supportedLocales: const [Locale('zh')], + locale: const Locale('zh'), + localizationsDelegates: GlobalMaterialLocalizations.delegates, + //builder: BotToastInit(), + builder: (context, child) { + ScreenUtil.setContext(context); + return MediaQuery( + //设置文字大小不随系统设置改变 + data: MediaQueryData.fromWindow(WidgetsBinding.instance.window).copyWith(textScaleFactor: 1.0), + child: BotToastInit().call(context, child), + ); + }, + navigatorObservers: [BotToastNavigatorObserver()], + ), + ), ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } diff --git a/lib/models/inner_models/base_llist_model.dart b/lib/models/inner_models/base_llist_model.dart new file mode 100644 index 0000000..3cd1d68 --- /dev/null +++ b/lib/models/inner_models/base_llist_model.dart @@ -0,0 +1,51 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'base_list_model.g.dart'; + +@JsonSerializable() +class BaseListModel { + final int code; + final String msg; + final ListInnerModel? data; + + BaseListModel({ + required this.code, + required this.msg, + required this.data, + }); + + factory BaseListModel.fromJson(Map json) => + _$BaseListModelFromJson(json); + + List get nullSafetyList { + if (data == null) { + return []; + } else if (data!.list == null) { + return []; + } else { + return data!.list!; + } + } + + int get nullSafetyTotal { + if (data == null) { + return 0; + } else { + return data!.total; + } + } +} + +@JsonSerializable() +class ListInnerModel { + final List? list; + final int total; + + ListInnerModel({ + this.list, + required this.total, + }); + + factory ListInnerModel.fromJson(Map json) => + _$ListInnerModelFromJson(json); +} diff --git a/lib/models/inner_models/base_modell.dart b/lib/models/inner_models/base_modell.dart new file mode 100644 index 0000000..68cc933 --- /dev/null +++ b/lib/models/inner_models/base_modell.dart @@ -0,0 +1,36 @@ +import 'package:dio/dio.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'base_model.g.dart'; + +@JsonSerializable() +class BaseModel { + int code; + String msg; + dynamic data; + BaseModel({ + required this.code, + required this.msg, + this.data, + }); + + + + List map(T Function(dynamic json) f) { + if (data == null) return []; + return (data! as List).map(f).toList(); + } + + factory BaseModel.unknown() => BaseModel( + code: -1, + msg: 'UNKNOW FAIL', + ); + + factory BaseModel.dioErr(DioError err) => BaseModel( + code: err.response?.statusCode ?? -1, + msg: err.message, + ); + + factory BaseModel.fromJson(Map json) => + _$BaseModelFromJson(json) as BaseModel; +} diff --git a/lib/models/user/user_info_model.dart b/lib/models/user/user_info_model.dart new file mode 100644 index 0000000..d74307c --- /dev/null +++ b/lib/models/user/user_info_model.dart @@ -0,0 +1,38 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:equatable/equatable.dart'; + +part 'user_info_model.g.dart'; + +@JsonSerializable() +class UserInfoModel extends Equatable { + final String inviteCode; + final String nickname; + final String headImg; + final int gender; + final String phone; + final int level; + + factory UserInfoModel.fromJson(Map json) => + _$UserInfoModelFromJson(json); + + Map toJson() => _$UserInfoModelToJson(this); + + const UserInfoModel({ + required this.inviteCode, + required this.nickname, + required this.headImg, + required this.gender, + required this.phone, + required this.level, + }); + + @override + List get props => [ + inviteCode, + nickname, + headImg, + gender, + phone, + level, + ]; +} diff --git a/lib/models/user/user_info_model.g.dart b/lib/models/user/user_info_model.g.dart new file mode 100644 index 0000000..2e20a98 --- /dev/null +++ b/lib/models/user/user_info_model.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user_info_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UserInfoModel _$UserInfoModelFromJson(Map json) => + UserInfoModel( + inviteCode: json['inviteCode'] as String, + nickname: json['nickname'] as String, + headImg: json['headImg'] as String, + gender: json['gender'] as int, + phone: json['phone'] as String, + level: json['level'] as int, + ); + +Map _$UserInfoModelToJson(UserInfoModel instance) => + { + 'inviteCode': instance.inviteCode, + 'nickname': instance.nickname, + 'headImg': instance.headImg, + 'gender': instance.gender, + 'phone': instance.phone, + 'level': instance.level, + }; diff --git a/lib/providers/user_provider.dart b/lib/providers/user_provider.dart new file mode 100644 index 0000000..514e5e8 --- /dev/null +++ b/lib/providers/user_provider.dart @@ -0,0 +1,59 @@ +import 'package:cloud_car_internal/models/user/user_info_model.dart'; +import 'package:cloud_car_internal/utils/api_client.dart'; +import 'package:cloud_car_internal/utils/hive_util.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; + +import '../constants/api/api.dart'; +import '../utils/toast/cloud_toast.dart'; + +class UserProvider extends ChangeNotifier { + bool _isLogin = false; + + bool get isLogin => _isLogin; + late UserInfoModel _userInfo; + + UserInfoModel get userInfo => _userInfo; + + Future init() async { + var appBox = await HiveUtil().openAppBox; + if (appBox.containsKey('token')) { + final token = appBox.get('token') as String; + _isLogin = true; + apiClient.setToken(token); + await updateUserInfo(); + return true; + } else { + _isLogin = false; + return false; + } + } + + Future setToken(String token, ) async { +var appBox = await HiveUtil().openAppBox; + apiClient.setToken(token); + await appBox.put('token', token); + _isLogin = true; + //每次打开app更新用户信息 + await updateUserInfo(); + } + + Future logout() async { + var appBox = await HiveUtil().openAppBox; + apiClient.clearToken(); + _isLogin = false; + await appBox.delete('token'); + } + + Future updateUserInfo() async { + // var base = await apiClient.request(API.user.userInfo); + // if (base.code == 0) { + // _userInfo = UserInfoModel.fromJson(base.data); + // } else { + // CloudToast.show(base.msg); + // _userInfo = UserInfoModel.fail; + // } + notifyListeners(); + } +} diff --git a/lib/utils/api_client.dart b/lib/utils/api_client.dart new file mode 100644 index 0000000..22e0262 --- /dev/null +++ b/lib/utils/api_client.dart @@ -0,0 +1,118 @@ +import 'dart:io'; + +import 'package:cloud_car_internal/constants/api/api.dart'; +import 'package:cloud_car_internal/models/inner_models/base_llist_model.dart'; +import 'package:cloud_car_internal/models/inner_models/base_modell.dart'; +import 'package:cloud_car_internal/utils/toast/cloud_toast.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:platform/platform.dart'; +import 'net_interceptor.dart'; + +final ApiClient apiClient = ApiClient(); + +class ApiClient { + late Dio _dio; + + Dio get dio => _dio; + + ApiClient() { + var headers = {}; + if (kIsWeb) { + headers = {'device-type': 'web'}; + } else { + headers = { + 'device-type': const LocalPlatform().operatingSystem.toLowerCase() + }; + } + + _dio = Dio() + ..options.baseUrl = API.baseURL + ..options.headers = headers + ..interceptors.add(NetworkInterceptor()); + } + + ///设置token + Dio setToken(String token) { + return _dio..options.headers[API.tokenKey] = token; + } + + ///清除token + Dio clearToken() { + return _dio + ..options.headers.removeWhere((key, value) => key == API.tokenKey); + } + + ///发送请求 + Future request( + String path, { + dynamic data, + bool showMessage = false, + }) async { + try { + var response = await _dio.post(path, data: data); + var baseModel = BaseModel.fromJson(response.data); + if (showMessage) CloudToast.show(baseModel.msg); + return baseModel; + } catch (e) { + if (e is DioError) { + return BaseModel.dioErr(e); + } + } + + return BaseModel.unknown(); + } + + ///请求列表 + Future requestList( + String path, { + dynamic data, + bool showMessage = false, + }) async { + var response = await _dio.post(path, data: data); + var baseModel = BaseListModel.fromJson(response.data); + if (showMessage) CloudToast.show(baseModel.msg); + return baseModel; + } + + ///上传图片 + Future uploadImage(File file) async { + var response = await _dio.post( + API.file.uploadImage, + data: FormData.fromMap( + {'image': await MultipartFile.fromFile(file.path)}, + ), + ); + var model = BaseModel.fromJson(response.data); + return model.data['path']; + } + + Future> uploadFiles( + List files, + ) async { + List urls = []; + if (files.isEmpty) { + return []; + } else { + for (var item in files) { + String path = await uploadImage(item); + if (path.isNotEmpty) { + urls.add(path); + } + } + } + return urls; + } + + ///上传文件 + Future uploadFile(File file) async { + var response = await _dio.post( + API.file.uploadFile, + data: FormData.fromMap( + {'file': await MultipartFile.fromFile(file.path)}, + ), + ); + var model = BaseModel.fromJson(response.data); + return model.data['path']; + } +} diff --git a/lib/utils/hive_util.dart b/lib/utils/hive_util.dart new file mode 100644 index 0000000..96be4ad --- /dev/null +++ b/lib/utils/hive_util.dart @@ -0,0 +1,26 @@ +import 'package:flutter/foundation.dart'; +import 'package:hive/hive.dart'; +import 'package:path_provider/path_provider.dart'; + +class HiveUtil { + static late final HiveUtil _instance = HiveUtil._(); + + factory HiveUtil() => _instance; + + HiveUtil._(); + + static init() async { + if (!kIsWeb) { + var dir = await getApplicationDocumentsDirectory(); + Hive.init(dir.path); + } + } + + Future get openAppBox async { + return await Hive.openBox('app'); + } + + Future get openUserBox async { + return await Hive.openBox('user'); + } +} diff --git a/lib/utils/net_interceptor.dart b/lib/utils/net_interceptor.dart new file mode 100644 index 0000000..635c42e --- /dev/null +++ b/lib/utils/net_interceptor.dart @@ -0,0 +1,46 @@ + +import 'package:cloud_car_internal/providers/user_provider.dart'; +import 'package:cloud_car_internal/utils/toast/cloud_toast.dart'; +import 'package:dio/dio.dart'; +import 'package:get/get.dart' hide Response; +import 'package:provider/provider.dart'; + +class NetworkInterceptor extends Interceptor { + @override + void onResponse(Response response, ResponseInterceptorHandler handler) async { + final userProvider = Provider.of(Get.context!, listen: false); + // LoggerData.addData(response); + int code = response.data['code'] ?? 0; + switch (code) { + //登录超时 + case 100000: + await userProvider.logout(); + CloudToast.show(response.data['msg']); + // await Get.offAll(() => const LoginPage()); + break; + default: + break; + } + super.onResponse(response, handler); + } + + @override + void onError(DioError err, ErrorInterceptorHandler handler) async { + switch (err.type) { + case DioErrorType.connectTimeout: + case DioErrorType.sendTimeout: + case DioErrorType.receiveTimeout: + CloudToast.show('连接超时'); + break; + case DioErrorType.response: + CloudToast.show('Server Err'); + break; + case DioErrorType.cancel: + break; + case DioErrorType.other: + CloudToast.show('网络出现问题'); + break; + } + super.onError(err, handler); + } +} diff --git a/lib/utils/toast/cloud_toast.dart b/lib/utils/toast/cloud_toast.dart new file mode 100644 index 0000000..238a0b8 --- /dev/null +++ b/lib/utils/toast/cloud_toast.dart @@ -0,0 +1,30 @@ + +import 'package:bot_toast/bot_toast.dart'; +import 'package:flutter/material.dart'; + +class CloudToast { + static show(String text,{AlignmentGeometry? align}) { + BotToast.showText( + text: text, + align: align?? const Alignment(0, 0.8), + borderRadius: BorderRadius.circular(20), + ); + } + + static Function get loading { + return BotToast.showCustomLoading( + toastBuilder: (cancel) { + return const Center( + child: Material( + clipBehavior: Clip.antiAlias, + shape: StadiumBorder(), + child: Padding( + padding: EdgeInsets.all(8.0), + child: CircularProgressIndicator(), + ), + ), + ); + }, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 553e69b..d39009a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,34 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "40.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" async: dependency: transitive description: @@ -8,6 +36,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.8.2" + basics: + dependency: transitive + description: + name: basics + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.0" boolean_selector: dependency: transitive description: @@ -22,6 +57,62 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.2" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + build_daemon: + dependency: transitive + description: + name: build_daemon + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + build_runner: + dependency: "direct dev" + description: + name: build_runner + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.11" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + url: "https://pub.dartlang.org" + source: hosted + version: "7.2.3" + built_collection: + dependency: transitive + description: + name: built_collection + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + url: "https://pub.dartlang.org" + source: hosted + version: "8.3.2" characters: dependency: transitive description: @@ -36,6 +127,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.5" clock: dependency: transitive description: @@ -43,6 +148,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + code_builder: + dependency: transitive + description: + name: code_builder + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" collection: dependency: transitive description: @@ -50,8 +162,15 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.16.0" - common_utils: + color: dependency: transitive + description: + name: color + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + common_utils: + dependency: "direct main" description: name: common_utils url: "https://pub.dartlang.org" @@ -78,6 +197,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.5" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.3" + dartx: + dependency: transitive + description: + name: dartx + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" decimal: dependency: transitive description: @@ -85,6 +218,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.0" + device_info: + dependency: transitive + description: + name: device_info + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + device_info_platform_interface: + dependency: transitive + description: + name: device_info_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" dio: dependency: "direct main" description: @@ -92,6 +239,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.6" + equatable: + dependency: "direct main" + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" extended_image: dependency: "direct main" description: @@ -127,6 +281,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.1.2" + file_utils: + dependency: transitive + description: + name: file_utils + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" flustars: dependency: "direct main" description: @@ -146,6 +314,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.1" + flutter_gen_core: + dependency: transitive + description: + name: flutter_gen_core + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.1+1" + flutter_gen_runner: + dependency: "direct dev" + description: + name: flutter_gen_runner + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.1+1" flutter_lints: dependency: "direct dev" description: @@ -158,6 +340,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_native_splash: + dependency: "direct dev" + description: + name: flutter_native_splash + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.3" flutter_screenutil: dependency: "direct main" description: @@ -170,11 +359,69 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_ume: + dependency: "direct dev" + description: + name: flutter_ume + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0+1" + flutter_ume_kit_console: + dependency: "direct dev" + description: + name: flutter_ume_kit_console + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" + flutter_ume_kit_device: + dependency: "direct dev" + description: + name: flutter_ume_kit_device + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" + flutter_ume_kit_dio: + dependency: "direct dev" + description: + name: flutter_ume_kit_dio + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" + flutter_ume_kit_perf: + dependency: "direct dev" + description: + name: flutter_ume_kit_perf + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" + flutter_ume_kit_show_code: + dependency: "direct dev" + description: + name: flutter_ume_kit_show_code + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" + flutter_ume_kit_ui: + dependency: "direct dev" + description: + path: "kits/flutter_ume_kit_ui" + ref: HEAD + resolved-ref: e56a59dccdf49e0b1074a76ac9f96109b1a7fdcc + url: "https://github.com/jiagengArctuition/flutter_ume" + source: git + version: "0.3.0+1" flutter_web_plugins: dependency: transitive description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" get: dependency: "direct main" description: @@ -182,6 +429,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.6.5" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + globbing: + dependency: transitive + description: + name: globbing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + grinder: + dependency: "direct dev" + description: + name: grinder + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.1" hive: dependency: "direct main" description: @@ -196,6 +471,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + hive_generator: + dependency: "direct dev" + description: + name: hive_generator + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.3" http: dependency: transitive description: @@ -210,6 +492,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.2" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" http_parser: dependency: transitive description: @@ -217,6 +506,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.1" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.3" + import_sorter: + dependency: "direct dev" + description: + name: import_sorter + url: "https://pub.dartlang.org" + source: hosted + version: "4.6.0" intl: dependency: "direct main" description: @@ -224,6 +527,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.17.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" js: dependency: transitive description: @@ -238,6 +548,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.5.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + url: "https://pub.dartlang.org" + source: hosted + version: "6.2.0" lints: dependency: transitive description: @@ -245,6 +562,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" matcher: dependency: transitive description: @@ -266,6 +590,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.7.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" nested: dependency: transitive description: @@ -273,6 +604,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" package_info_plus: dependency: "direct main" description: @@ -316,14 +654,14 @@ packages: source: hosted version: "1.0.5" path: - dependency: transitive + dependency: "direct dev" description: name: path url: "https://pub.dartlang.org" source: hosted version: "1.8.1" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider url: "https://pub.dartlang.org" @@ -406,8 +744,15 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.0" - platform: + petitparser: dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.0" + platform: + dependency: "direct main" description: name: platform url: "https://pub.dartlang.org" @@ -420,6 +765,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.0" process: dependency: transitive description: @@ -434,6 +786,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.0.3" + pub_semver: + dependency: "direct dev" + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" rational: dependency: transitive description: @@ -441,6 +814,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.0" + share: + dependency: transitive + description: + name: share + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" shared_preferences: dependency: transitive description: @@ -497,11 +877,39 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.2" + source_helper: + dependency: transitive + description: + name: source_helper + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.2" source_span: dependency: transitive description: @@ -530,6 +938,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" string_scanner: dependency: transitive description: @@ -544,6 +959,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0+2" + system_info: + dependency: transitive + description: + name: system_info + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" term_glyph: dependency: transitive description: @@ -558,6 +980,41 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.9" + time: + dependency: transitive + description: + name: time + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + timing: + dependency: transitive + description: + name: timing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + tint: + dependency: transitive + description: + name: tint + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + touch_indicator: + dependency: transitive + description: + name: touch_indicator + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + tuple: + dependency: transitive + description: + name: tuple + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" typed_data: dependency: transitive description: @@ -565,6 +1022,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + universal_io: + dependency: transitive + description: + name: universal_io + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" vector_math: dependency: transitive description: @@ -572,6 +1036,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + vm_service: + dependency: transitive + description: + name: vm_service + url: "https://pub.dartlang.org" + source: hosted + version: "7.5.0" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" win32: dependency: transitive description: @@ -586,6 +1071,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.0+1" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "5.4.1" + yaml: + dependency: "direct dev" + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" sdks: dart: ">=2.17.0 <3.0.0" flutter: ">=2.8.1" diff --git a/pubspec.yaml b/pubspec.yaml index 5d62e89..cfa5749 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,15 +39,45 @@ dependencies: extended_image: ^7.0.0-dev.1 #json model生成注解 json_annotation: ^4.4.0 - + #设备平台信息 + platform: ^3.1.0 + #路径 + path_provider: ^2.0.9 + #生成类重载==运算符和toString方法 + equatable: ^2.0.3 + #工具类 + common_utils: ^2.1.0 cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: sdk: flutter - + #应用内调试工具 + flutter_ume: ^0.3.0+1 + flutter_ume_kit_ui: + git: + url: https://github.com/jiagengArctuition/flutter_ume + path: kits/flutter_ume_kit_ui + flutter_ume_kit_device: ^0.3.0 + flutter_ume_kit_perf: ^0.3.0 + flutter_ume_kit_show_code: ^0.3.0 + flutter_ume_kit_console: ^0.3.0 + flutter_ume_kit_dio: ^0.3.0 + #导入包整理 + import_sorter: ^4.5.1 + hive_generator: ^1.1.0 + #脚本工具 + grinder: ^0.9.1 + flutter_native_splash: ^1.1.8+4 + #model自动生成 + json_serializable: ^6.1.3 + build_runner: ^2.0.2 + yaml: ^3.1.0 + pub_semver: ^2.0.0 + flutter_gen_runner: ^4.1.3 flutter_lints: ^2.0.0 + path: ^1.8.1 flutter: diff --git a/tool/_build.dart b/tool/_build.dart new file mode 100644 index 0000000..60a902c --- /dev/null +++ b/tool/_build.dart @@ -0,0 +1,76 @@ +part of './grind.dart'; + +@Task('打包Android项目') +buildApk() async { + await runAsync( + 'fvm', + arguments: [ + 'flutter', + 'build', + 'apk', + '--target-platform=android-arm64', + '--dart-define', + 'ENV=release', + ], + ); + + String date = DateUtil.formatDate(DateTime.now(), format: 'yy_MM_dd_HH_mm'); + String version = await getVersion(); + await runAsync('rm', arguments: ['-rf', Config.apkDir]); + await runAsync('mkdir', arguments: ['-p', Config.apkDir]); + await runAsync('mv', arguments: [ + Config.buildPath, + '${Config.apkDir}/${Config.packageName}_${version}_release_$date.apk' + ]); +} + +@Task('打包Android项目') +buildApkDev() async { + await runAsync( + 'fvm', + arguments: [ + 'flutter', + 'build', + 'apk', + '--target-platform=android-arm64', + '--dart-define', + 'ENV=dev', + ], + ); + String date = DateUtil.formatDate(DateTime.now(), format: 'yy_MM_dd_HH_mm'); + String version = await getVersion(); + await runAsync('rm', arguments: ['-rf', Config.apkDevDir]); + await runAsync('mkdir', arguments: ['-p', Config.apkDevDir]); + await runAsync('mv', arguments: [ + Config.buildPath, + '${Config.apkDevDir}/${Config.packageName}_${version}_beta_$date.apk' + ]); +} + +@Task('打包iOS项目') +buildIos() async { + await runAsync( + 'fvm', + arguments: [ + 'flutter', + 'build', + 'ios', + '--dart-define', + 'ENV=release', + ], + ); +} + +@Task('打包iOS项目') +buildIosDev() async { + await runAsync( + 'fvm', + arguments: [ + 'flutter', + 'build', + 'ios', + '--dart-define', + 'ENV=dev', + ], + ); +} diff --git a/tool/_project_manage.dart b/tool/_project_manage.dart new file mode 100644 index 0000000..26c077d --- /dev/null +++ b/tool/_project_manage.dart @@ -0,0 +1,64 @@ +part of 'grind.dart'; + +@Task('import 排序') +void sort() { + Pub.run('import_sorter:main'); +} + +@Task('格式化dart代码') +void format() { + DartFmt.format(libDir); +} + +@Task('自动提交修改') +@Depends(sort, format, gitPush) +void git() { + log(' commit to git'); + run( + 'git', + arguments: [ + 'commit', + '-a', + '-m', + '[auto task] sort & format', + ], + ); +} + +@Task('推送代码') +void gitPush() { + log(' push to git'); + run( + 'git', + arguments: ['push'], + ); +} + +@Task('build runner') +void gen() async { + await runAsync( + 'fvm', + arguments: [ + 'flutter', + 'pub', + 'run', + 'build_runner', + 'build', + ], + ); +} + +@Task('build runner clean') +void genClean() async { + await runAsync( + 'fvm', + arguments: [ + 'flutter', + 'pub', + 'run', + 'build_runner', + 'build', + '--delete-conflicting-outputs' + ], + ); +} diff --git a/tool/config.dart b/tool/config.dart new file mode 100644 index 0000000..166953d --- /dev/null +++ b/tool/config.dart @@ -0,0 +1,14 @@ +class Config { + ///包名 + static const String packageName = 'cloud_car'; + + ///打包目录 + static String get buildPath => + './build/app/outputs/flutter-apk/app-release.apk'; + + ///测试包文件夹 + static String get apkDevDir => '../team/cloud_car_internal/dev'; + + ///正式包文件夹 + static String get apkDir => '../team/cloud_car_internal/release'; +} diff --git a/tool/grind.dart b/tool/grind.dart new file mode 100644 index 0000000..09116d9 --- /dev/null +++ b/tool/grind.dart @@ -0,0 +1,68 @@ +import 'dart:io'; + +import 'package:grinder/grinder.dart'; +import 'package:path/path.dart'; +import 'package:pub_semver/pub_semver.dart'; +import 'package:yaml/yaml.dart'; +import 'config.dart'; +import 'version_tool.dart'; +import 'package:common_utils/common_utils.dart'; +part '_build.dart'; +part '_project_manage.dart'; + +main(args) => grind(args); + +@Task('add minor version number') +void addVersion() async { + String projectPath = Directory('.').absolute.path; + String yamlPath = join(projectPath, 'pubspec.yaml'); + String yamlContent = await File(yamlPath).readAsString(); + dynamic content = loadYaml(yamlContent); + String version = content['version']; + //rename version + + Version resultVersion = VersionTool.fromText(version).nextMinorTag('dev'); + + String result = yamlContent.replaceFirst(version, resultVersion.toString()); + await File(yamlPath).writeAsString(result); +} + +@Task('add path version number') +void addVersionPatch() async { + String projectPath = Directory('.').absolute.path; + String yamlPath = join(projectPath, 'pubspec.yaml'); + String yamlContent = await File(yamlPath).readAsString(); + dynamic content = loadYaml(yamlContent); + String version = content['version']; + //rename version + + Version resultVersion = VersionTool.fromText(version).nextPatchTag('dev'); + + String result = yamlContent.replaceFirst(version, resultVersion.toString()); + await File(yamlPath).writeAsString(result); +} + +@Task('add major version number') +void addVersionMajor() async { + String projectPath = Directory('.').absolute.path; + String yamlPath = join(projectPath, 'pubspec.yaml'); + String yamlContent = await File(yamlPath).readAsString(); + dynamic content = loadYaml(yamlContent); + String version = content['version']; + //rename version + + Version resultVersion = VersionTool.fromText(version).nextMajorTag('dev'); + + String result = yamlContent.replaceFirst(version, resultVersion.toString()); + await File(yamlPath).writeAsString(result); +} + +@Task() +Future getVersion() async { + String projectPath = Directory('.').absolute.path; + String yamlPath = join(projectPath, 'pubspec.yaml'); + String yamlContent = await File(yamlPath).readAsString(); + dynamic content = loadYaml(yamlContent); + String version = content['version']; + return version; +} diff --git a/tool/version_tool.dart b/tool/version_tool.dart new file mode 100644 index 0000000..94ad847 --- /dev/null +++ b/tool/version_tool.dart @@ -0,0 +1,59 @@ +import 'package:pub_semver/pub_semver.dart'; + +enum VersionNumber { + major, + minor, + patch, +} + +class VersionTool { + Version version; + VersionTool(this.version); + VersionTool.fromText(String text) : version = Version.parse(text); + + Version get nextMajor => _addBuildNumber(VersionNumber.major); + Version get nextMinor => _addBuildNumber(VersionNumber.minor); + Version get nextPatch => _addBuildNumber(VersionNumber.patch); + + Version nextMajorTag(String tag) => _addBuildNumber( + VersionNumber.major, + tag: tag, + ); + Version nextMinorTag(String tag) => _addBuildNumber( + VersionNumber.minor, + tag: tag, + ); + Version nextPatchTag(String tag) => _addBuildNumber( + VersionNumber.patch, + tag: tag, + ); + + Version _addBuildNumber(VersionNumber type, {String? tag}) { + switch (type) { + case VersionNumber.major: + return Version( + version.major, + version.minor, + version.patch + 1, + pre: tag, + build: '${(version.build.first as int) + 1}', + ); + case VersionNumber.minor: + return Version( + version.major, + version.minor + 1, + 0, + pre: tag, + build: '${(version.build.first as int) + 1}', + ); + case VersionNumber.patch: + return Version( + version.major + 1, + 0, + 0, + pre: tag, + build: '${(version.build.first as int) + 1}', + ); + } + } +}