master
parent
5baa58d3f6
commit
54fdafd329
@ -0,0 +1 @@
|
||||
/Users/zhangmeng/fvm/versions/3.0.0
|
@ -0,0 +1,4 @@
|
||||
{
|
||||
"flutterSdkVersion": "3.0.0",
|
||||
"flavors": {}
|
||||
}
|
@ -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';
|
||||
}
|
@ -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: <ThemeExtension<dynamic>>[
|
||||
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<Color?>((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<MyAppStyle> {
|
||||
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<MyAppStyle> lerp(ThemeExtension<MyAppStyle>? 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,
|
||||
});
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'base_list_model.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class BaseListModel<T> {
|
||||
final int code;
|
||||
final String msg;
|
||||
final ListInnerModel? data;
|
||||
|
||||
BaseListModel({
|
||||
required this.code,
|
||||
required this.msg,
|
||||
required this.data,
|
||||
});
|
||||
|
||||
factory BaseListModel.fromJson(Map<String, dynamic> 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<dynamic>? list;
|
||||
final int total;
|
||||
|
||||
ListInnerModel({
|
||||
this.list,
|
||||
required this.total,
|
||||
});
|
||||
|
||||
factory ListInnerModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$ListInnerModelFromJson(json);
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'base_model.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class BaseModel<T> {
|
||||
int code;
|
||||
String msg;
|
||||
dynamic data;
|
||||
BaseModel({
|
||||
required this.code,
|
||||
required this.msg,
|
||||
this.data,
|
||||
});
|
||||
|
||||
|
||||
|
||||
List<T> map(T Function(dynamic json) f) {
|
||||
if (data == null) return [];
|
||||
return (data! as List<T>).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<String, dynamic> json) =>
|
||||
_$BaseModelFromJson(json) as BaseModel<T>;
|
||||
}
|
@ -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<String, dynamic> json) =>
|
||||
_$UserInfoModelFromJson(json);
|
||||
|
||||
Map<String, dynamic> 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<Object?> get props => [
|
||||
inviteCode,
|
||||
nickname,
|
||||
headImg,
|
||||
gender,
|
||||
phone,
|
||||
level,
|
||||
];
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'user_info_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
UserInfoModel _$UserInfoModelFromJson(Map<String, dynamic> 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<String, dynamic> _$UserInfoModelToJson(UserInfoModel instance) =>
|
||||
<String, dynamic>{
|
||||
'inviteCode': instance.inviteCode,
|
||||
'nickname': instance.nickname,
|
||||
'headImg': instance.headImg,
|
||||
'gender': instance.gender,
|
||||
'phone': instance.phone,
|
||||
'level': instance.level,
|
||||
};
|
@ -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<bool> 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();
|
||||
}
|
||||
}
|
@ -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 = <String, dynamic>{};
|
||||
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<BaseModel> 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<BaseListModel> 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<String> 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<List<String>> uploadFiles(
|
||||
List<File> files,
|
||||
) async {
|
||||
List<String> 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<String> 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'];
|
||||
}
|
||||
}
|
@ -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<Box> get openAppBox async {
|
||||
return await Hive.openBox('app');
|
||||
}
|
||||
|
||||
Future<Box> get openUserBox async {
|
||||
return await Hive.openBox('user');
|
||||
}
|
||||
}
|
@ -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<UserProvider>(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);
|
||||
}
|
||||
}
|
@ -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(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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',
|
||||
],
|
||||
);
|
||||
}
|
@ -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'
|
||||
],
|
||||
);
|
||||
}
|
@ -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';
|
||||
}
|
@ -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<String> 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;
|
||||
}
|
@ -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}',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue