|
|
import 'dart:async';
|
|
|
import 'dart:convert';
|
|
|
import 'dart:developer' as developer;
|
|
|
import 'dart:io';
|
|
|
import 'dart:math';
|
|
|
|
|
|
import 'package:ai_save_account/ai_save_account.dart';
|
|
|
import 'package:audioplayers/audio_cache.dart';
|
|
|
import 'package:camera/camera.dart';
|
|
|
import 'package:convert/convert.dart';
|
|
|
import 'package:crypto/crypto.dart' as crypto;
|
|
|
import 'package:device_info/device_info.dart';
|
|
|
import 'package:flutter/material.dart';
|
|
|
import 'package:flutter/services.dart';
|
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
|
|
|
|
//import '../my_wechat_assets_picker_fix/my_asset_picker_1.dart';
|
|
|
import 'package:fluttertoast/fluttertoast.dart';
|
|
|
import 'package:hyzp_ybqx/provider/player_region.dart';
|
|
|
|
|
|
import 'UserInfo.dart';
|
|
|
import 'dioFun.dart';
|
|
|
|
|
|
////////////////////////////////////////////
|
|
|
// begin hyzp_ybqx-Commit022-区县切换新方法-OK
|
|
|
|
|
|
// 1、修改手机桌面的App图标文本
|
|
|
// R:\FlutterProject\FlutterProject51-hyzp_ybqx\hyzp_ybqx\android\app\src\main\AndroidManifest.xml
|
|
|
// android:label="宜宾黑烟抓拍"
|
|
|
// android:label="宜宾市翠屏黑烟抓拍"
|
|
|
// android:label="宜宾三江新区黑烟抓拍"
|
|
|
// android:label="宜宾市长宁黑烟抓拍"
|
|
|
// android:label="宜宾市筠连黑烟抓拍"
|
|
|
// android:label="宜宾市兴文黑烟抓拍"
|
|
|
|
|
|
// 2、修改App的android和Flutter启动图片,制作并运行 hyzp_ybqx.images_copy.cmd,自动完成两项拷贝任务
|
|
|
// (1)、拷贝不同分辨率的图片文件hyzp_ybqx_launche.png到下面目录,作为App的android启动图片
|
|
|
// r:\FlutterProject\FlutterProject51-hyzp_ybqx\hyzp_ybqx\android\app\src\main\res\mipmap-hdpi\
|
|
|
// r:\FlutterProject\FlutterProject51-hyzp_ybqx\hyzp_ybqx\android\app\src\main\res\mipmap-mdpi\
|
|
|
// r:\FlutterProject\FlutterProject51-hyzp_ybqx\hyzp_ybqx\android\app\src\main\res\mipmap-xhdpi\
|
|
|
// r:\FlutterProject\FlutterProject51-hyzp_ybqx\hyzp_ybqx\android\app\src\main\res\mipmap-xxhdpi\
|
|
|
// r:\FlutterProject\FlutterProject51-hyzp_ybqx\hyzp_ybqx\android\app\src\main\res\mipmap-xxxhdpi\
|
|
|
// (2)、拷贝 750 * 1334 的图片文件到下面目录,作为App的Flutter启动图片
|
|
|
// r:\FlutterProject\FlutterProject51-hyzp_ybqx\hyzp_ybqx\assets\images\hyzp_ybqx01_cuiping_launche.png
|
|
|
|
|
|
// 3、修改文本变量
|
|
|
// 位于文件 R:\FlutterProject\FlutterProject51-hyzp_ybqx\hyzp_ybqx\lib\components\commonFun.dart 中
|
|
|
// 511501、宜宾市
|
|
|
// String APPkey = 'ijddvzgEGaxbzsbmCtpdohxHyrAArwJB'; // 宜宾市APPkey
|
|
|
// String service_tel = '\n服务热线:187-8467-8300';
|
|
|
// String yibin_QuXian = '宜宾';
|
|
|
// String copyright_info = '© ' + yibin_QuXian + '市生态环境局 四川省踏石科技有限公司 版权所有' + service_tel;
|
|
|
// String copyright_info_PinYin = 'YIBIN BLACK SMOKE CAR CAPTURE SYSTEM';
|
|
|
|
|
|
// const serviceUrl_ybqx = 'http://125.64.218.67:9904'; // 宜宾市
|
|
|
// const serviceUrl_ybqx_media = 'http://125.64.218.67:9908/'; // 宜宾市
|
|
|
|
|
|
// 区县APPkey
|
|
|
String APPkey = 'pdohxHyr79ddvzgE8ArwGaxb01bmCtJB';
|
|
|
// 区县后台地址
|
|
|
const serviceUrl_ybqx = 'http://125.64.218.67:9909';
|
|
|
|
|
|
// 区县违章图片和视频地址前缀
|
|
|
// const serviceUrl_ybqx_media = 'http://125.64.218.67:9912/';
|
|
|
// 20220320,罗腾-子非鱼: @闵军 区县端口视频文件和图片文件的前缀,由原来的 http://125.64.218.67:9912/ 调整为 http://125.64.218.67:9906/
|
|
|
// 罗腾-子非鱼: 9912端口已被禁用
|
|
|
const serviceUrl_ybqx_media = 'http://125.64.218.67:9906/';
|
|
|
|
|
|
// 区县球机方向控制地址
|
|
|
// const String setSphericalCameraUrl = 'http://125.64.218.67:9914/'; //原来的
|
|
|
|
|
|
// 罗腾-子非鱼:因环保局缩减了我们的端口数量,现在对控制球机的接口进行调整。
|
|
|
// 1、控制端口由原来的 9914 调整为 9903;
|
|
|
// 2、新增sip参数,0表示市级平台,1表示区县平台。之前是通过不同端口来分别访问市级和区县的,因缩减了我们的端口数,现在统一使用1个端口,通过sip参数来区分市级和区县
|
|
|
const String setSphericalCameraUrl = 'http://125.64.218.67:9903/'; //新版的
|
|
|
const int g_sip = 1; // 新增sip参数,0表示市级平台,1表示区县平台。
|
|
|
|
|
|
///用于定时提醒的变量和函数
|
|
|
//获取待审核黑烟车记录
|
|
|
List listReviewed = [];
|
|
|
bool g_bVoiceRemind = false; // 用户设置是否开启语音提醒
|
|
|
int g_remindGap = 60; // 提醒间隔默认为60S,0表示取消定时提醒
|
|
|
Timer g_remindTimer; //定时提醒变量
|
|
|
|
|
|
// 设置定时提醒
|
|
|
setRemindTimer({bool enable = true}) {
|
|
|
// 先取消旧的定时任务
|
|
|
if (null != g_remindTimer) {
|
|
|
g_remindTimer.cancel(); //取消计时器
|
|
|
}
|
|
|
|
|
|
// 设置新的定时任务
|
|
|
if (enable && g_remindGap > 0) {
|
|
|
///循环执行定时任务,间隔 g_remindGap 秒
|
|
|
g_remindTimer = Timer.periodic(Duration(seconds: g_remindGap), (timer) {
|
|
|
///定时任务
|
|
|
doRemind();
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 执行语音提醒
|
|
|
Future doRemind() async {
|
|
|
// 获取待审核黑烟车记录
|
|
|
if (g_bVoiceRemind) {
|
|
|
listReviewed.clear();
|
|
|
print("listReviewed = $listReviewed");
|
|
|
await getReviewedList(); //注意:访问区县后台接口,需要统一添加区县代码参数
|
|
|
print("listReviewed = $listReviewed");
|
|
|
// if (g_bVoiceRemind) {
|
|
|
// import 'dart:io';
|
|
|
// import 'package:audioplayers/audio_cache.dart';
|
|
|
// print("播放:语音提醒.mp3");
|
|
|
if (listReviewed.length > 0) {
|
|
|
AudioCache().play(File('audio/语音提醒.mp3').path); //语音提醒
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
///用于定时提醒的变量和函数
|
|
|
|
|
|
// {
|
|
|
// "ret": 200,
|
|
|
// "data": {
|
|
|
// "is_login": true,
|
|
|
// "user_id": 152,
|
|
|
// "token": "959091E2A5E362E22F22F8DBE05737A95545A43AF28B19AB3DAD5F8557EE2E29",
|
|
|
// "qx_code": "511528",
|
|
|
// "qx_name": "兴文县"
|
|
|
// },
|
|
|
// "msg": ""
|
|
|
// }
|
|
|
|
|
|
// 一套APP适应多个区县,等用户登录后,会返回 qx_code、qx_name
|
|
|
int qx_code = -1;
|
|
|
String qx_name = '';
|
|
|
String qx_name_long = '';
|
|
|
int qx_trial_tag = 0;
|
|
|
String service_tel = '';
|
|
|
String copyright_info = '';
|
|
|
String copyright_info_PinYin = '';
|
|
|
// 区县中心地址
|
|
|
double center_latitude = -1; // 区县中心纬度
|
|
|
double center_longitude = -1; // 区县中心经度
|
|
|
|
|
|
clear_user_info() {
|
|
|
qx_code = -1;
|
|
|
qx_name = '';
|
|
|
qx_name_long = '';
|
|
|
qx_trial_tag = 0;
|
|
|
service_tel = '';
|
|
|
copyright_info = '';
|
|
|
copyright_info_PinYin = '';
|
|
|
// 区县中心地址
|
|
|
center_latitude = -1; // 区县中心纬度
|
|
|
center_longitude = -1; // 区县中心经度
|
|
|
}
|
|
|
|
|
|
// 511528、兴文县
|
|
|
// int qx_code = 511528;
|
|
|
// String qx_name = '宜宾市兴文';
|
|
|
// String service_tel = '\n服务热线:187-8467-8300';
|
|
|
// String copyright_info = '© ' + qx_name + '生态环境局 四川省踏石科技 版权所有' + service_tel;
|
|
|
// String copyright_info_PinYin = 'YIBIN XINGWEN BLACK SMOKE CAR CAPTURE SYSTEM';
|
|
|
// // 兴文县中心地址:天泉商城
|
|
|
// double center_latitude = 28.29678023715008; // 区县中心纬度
|
|
|
// double center_longitude = 105.24189826141459; // 区县中心经度
|
|
|
|
|
|
// 注意:百度官方发布的城市中心点坐标是经度在前、纬度在后,必须对调才行,否则无法正确显示指定城市的地图
|
|
|
// BMFCoordinate BMFCoordinate(double latitude (纬度), double longitude (经度)) //中国领域一般经度大些
|
|
|
// center: BMFCoordinate(28.77914, 104.644079), //宜宾市翠屏白塔山
|
|
|
|
|
|
// 在点位地图中点击某个标注,在控制台会显示该标注的纬度和经度
|
|
|
// 兴文县中心位置:I/flutter (12538): mapPoi = {text: 半岛和居, pt: {latitude: 28.29678023715008, longitude: 105.24189826141459}, uid: b9e39ee2e8304f872fe67bcf}
|
|
|
// 兴文县5个点位坐标
|
|
|
// 序号 选址位置 车道数量 经纬度 用途
|
|
|
// 1 古高路骨科医院附近 双向6车道 105.19762 28.334613 抓拍自长宁、江安、珙县方向和古宜高速入城黑烟车辆
|
|
|
// 2 高铁站附近 双向4车道 105.239899 28.337284 抓拍自泸州纳溪区方向入城黑烟车辆
|
|
|
// 3 大礼村附近 双向2车道 105.270567 28.285734 抓拍自泸州叙永县方向入城黑烟车辆
|
|
|
// 4 石海收费站附近 双向4车道 105.251038 28.287758 抓拍自古宜高速入城黑烟车辆
|
|
|
// 5 温水溪加油站附近 双向2车道 105.246259 28.286645 抓拍自云南威信县方向入城黑烟车辆
|
|
|
|
|
|
// 4、全局替换,将 ”hyzp_ybqx“全部替换为 ”hyzp_ybqx“,自动完成以下修改
|
|
|
// (1)、修改 R:\FlutterProject\FlutterProject51-hyzp_ybqx\hyzp_ybqx\pubspec.yaml 文件中的AppID,但存放目錄不變
|
|
|
// name: hyzp_ybqx
|
|
|
// (2)、全局替换:
|
|
|
// A、将 “com.flutter.hyzp_ybqx00_yibin” 全部替换为 “com.flutter.hyzp_ybqx”
|
|
|
// B、将 “package:hyzp_ybqx00_yibin/” 全部替换为 “package:hyzp_ybqx/”
|
|
|
// C、将 “# hyzp_ybqx00_yibin” 全部替换为 “# hyzp_ybqx”
|
|
|
// D、将 “<string>hyzp_ybqx00_yibin</string>” 全部替换为 “<string>hyzp_ybqx</string>”
|
|
|
|
|
|
// 5、完成以上修改后,打开 Android Studio 的终端窗口,切换到项目的 lib 目录下,运行 flutter clean
|
|
|
|
|
|
// 6、重新编译运行App
|
|
|
|
|
|
// end hyzp_ybqx-Commit022-区县切换新方法-OK
|
|
|
////////////////////////////////////////////
|
|
|
|
|
|
//LED字幕信息
|
|
|
//String g_ledMessage = '绿水青山就是金山银山 宜宾市翠屏生态环境局宣。';
|
|
|
|
|
|
// 是否已经调用 FlutterDownloader.initialize(debug: true)
|
|
|
bool bFlutterDownloader_initialize = false;
|
|
|
bool bNewVer = false; //是否发现新版本
|
|
|
|
|
|
//处理延时登录,判断从网络获取三种统计数据是否完成
|
|
|
bool bMayLogin = true;
|
|
|
//处理延时登录,判断是否已经点击登录按钮
|
|
|
bool bPreLoading = false;
|
|
|
//处理延时登录,判断用户名登录是否验证通过
|
|
|
bool bLoginVerify = false;
|
|
|
|
|
|
bool bHasMore = true;
|
|
|
|
|
|
//part library
|
|
|
//dart中,通过使用part、part of、library来实现拆分库,这样,就可以将一个庞大的库拆分成各种小库,只要引用主库即可
|
|
|
|
|
|
//点位总数
|
|
|
int dwSum = -1;
|
|
|
|
|
|
Size sizeWindowPhysicalSize;
|
|
|
|
|
|
//String dateAppCompile = '2020.12.30'; //1.0.1+1
|
|
|
//String dateAppCompile = '2021.02.20'; //1.2.5+1
|
|
|
//String dateAppCompile = '2021.03.18'; //1.2.6+1
|
|
|
//String dateAppCompile = '2021.05.18'; //1.2.7
|
|
|
List<String> g_list = [];
|
|
|
|
|
|
//正在获取点位视频标志,禁止重入
|
|
|
bool getingDwVideo = false;
|
|
|
int getCount = 1; //获取点位视频地址尝试次数
|
|
|
int getSumTime = 0; //获取点位视频地址耗时(秒)
|
|
|
int getingIndex = -1; //正在获取视频的点位的索引号
|
|
|
String getingDwmc = ''; //正在获取视频的点位名称
|
|
|
String urlnew =
|
|
|
"http://www.yibinu.edu.cn/__local/5/35/DF/264049B7E978EEE2F5849688986_05D4A6FE_152CDB8C.mp4?e=.mp4";
|
|
|
|
|
|
bool isVideoUrl(String url, {bool showToast = false}) {
|
|
|
print('url = $url');
|
|
|
if (0 == url.length) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
String prefix = url.substring(0, 4);
|
|
|
List list = ['http', 'rtmp', 'rstp'];
|
|
|
for (String item in list) {
|
|
|
if (prefix == item) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (showToast) {
|
|
|
Fluttertoast.showToast(
|
|
|
msg: '获取视频地址失败',
|
|
|
toastLength: Toast.LENGTH_SHORT,
|
|
|
gravity: ToastGravity.CENTER,
|
|
|
);
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
final TextEditingController myController = TextEditingController();
|
|
|
|
|
|
bool Playing = false; //禁止同时启动两次播放器
|
|
|
|
|
|
//final FijkPlayer player = FijkPlayer();
|
|
|
int g_iIndex = 0;
|
|
|
PlayerRegionProvide playerRegionProvide;
|
|
|
|
|
|
Future<void> sysPop() async {
|
|
|
// currentPos = player.currentPos.inMilliseconds; //seekto方法的参数是毫秒
|
|
|
// await writeCurrentPosFile();
|
|
|
// await player.stop();
|
|
|
await SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
|
|
}
|
|
|
|
|
|
//人脸注册和人脸识别登录成功标志
|
|
|
int faceReg = -1; //1 成功,0 失败,-1 处理中
|
|
|
int faceLogin = -1; //1 成功,0 失败,-1 处理中
|
|
|
|
|
|
//人脸注册时所需用户ID
|
|
|
int faceRegUserID = -1; //人脸注册时所需用户ID,-1 非法
|
|
|
|
|
|
List<CameraDescription> cameras;
|
|
|
List<UserAccount> g_users = new List(); //历史账号
|
|
|
bool g_can_expand_ListView = false; //是否能够打开历史账号
|
|
|
UserInfo g_userInfo = UserInfo(mapUserInfoRet: {
|
|
|
"ret": 200,
|
|
|
"data": {
|
|
|
"is_login": true,
|
|
|
"user_id": 152,
|
|
|
"token": "959091E2A5E362E22F22F8DBE05737A95545A43AF28B19AB3DAD5F8557EE2E29",
|
|
|
"qx_code": "511528",
|
|
|
"qx_name": "兴文县",
|
|
|
"sfsy": 0, //区县用户登录接口增加返回字段“sfsy”,0表示正式账号,1表示试用账号
|
|
|
},
|
|
|
"msg": ""
|
|
|
});
|
|
|
|
|
|
// 去除末尾的区县
|
|
|
String trim_county(String _name) {
|
|
|
return _name.substring(0, _name.length - 1);
|
|
|
}
|
|
|
|
|
|
Future<Map> getMapFromJson(var response) async {
|
|
|
String _str = json.encode(response);
|
|
|
Map _map = json.decode(_str);
|
|
|
return _map;
|
|
|
}
|
|
|
|
|
|
// Future<void> getVideoList(BuildContext context) async {
|
|
|
// List<AssetEntity> assets = <AssetEntity>[];
|
|
|
// return await MyAssetPicker.pickAssets(
|
|
|
// context,
|
|
|
// maxAssets: 1,
|
|
|
// selectedAssets: assets,
|
|
|
// requestType: RequestType.video,
|
|
|
// );
|
|
|
// }
|
|
|
|
|
|
Future<String> getAndroidId() async {
|
|
|
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
|
|
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
|
|
//print('每个手机唯一的设备号:${androidInfo.androidId}'); // e.g. "Moto G (4)"
|
|
|
g_userInfo.thisAndroidId = androidInfo.androidId;
|
|
|
return g_userInfo.thisAndroidId;
|
|
|
}
|
|
|
|
|
|
// void playOrPause() {
|
|
|
// playerRegionProvide.changePlayerState(bPlaying);
|
|
|
//
|
|
|
// if (bPlaying) {
|
|
|
// player.start();
|
|
|
// } else {
|
|
|
// player.pause();
|
|
|
// }
|
|
|
//
|
|
|
// Storage.setString('bFirstPlay', bFirstPlay ? 'true' : 'false');
|
|
|
// Storage.setString('bPlaying', bPlaying ? 'true' : 'false');
|
|
|
//
|
|
|
// //updateFile();
|
|
|
// }
|
|
|
//
|
|
|
// void playAndPause() {
|
|
|
// if (player.state == FijkState.started) {
|
|
|
// bPlaying = false;
|
|
|
// player.pause();
|
|
|
// } else if (player.state == FijkState.paused) {
|
|
|
// bPlaying = true;
|
|
|
// player.start();
|
|
|
// }
|
|
|
// //setState(() {});
|
|
|
// playerRegionProvide.changePlayerState(bPlaying);
|
|
|
// Storage.setString('bPlaying', bPlaying ? 'true' : 'false');
|
|
|
// }
|
|
|
|
|
|
Alignment getAlignment(Offset offset, Size size) {
|
|
|
// final double centerX = offset.dx / 2.0;
|
|
|
// final double centerY = offset.dy / 2.0;
|
|
|
|
|
|
//offset.dx = centerX + alignment.x * centerX;
|
|
|
//double alignmentX = (0.0 == centerX) ? 0.0 : ((offset.dx - centerX) / centerX);
|
|
|
double alignmentX = (offset.dx / size.width).clamp(-1.0, 1.0);
|
|
|
|
|
|
//offset.dy = centerY + alignment.y * centerY;
|
|
|
//double alignmentY = (0.0 == centerY) ? 0.0 : ((offset.dy - centerY) / centerY);
|
|
|
double alignmentY = (offset.dy / size.height).clamp(-1.0, 1.0);
|
|
|
|
|
|
// print('offset.dx = ${offset.dx}, offset.dy = ${offset.dy}');
|
|
|
print('Alignment.X = $alignmentX, Alignment.Y = $alignmentY');
|
|
|
return Alignment(alignmentX, alignmentY);
|
|
|
}
|
|
|
|
|
|
//flutter (dart)生成N位随机数
|
|
|
//https://blog.csdn.net/qq_36071410/article/details/101268640
|
|
|
// 庄童 2019-09-24 10:24:58 10271 收藏
|
|
|
String RandomBit(int len) {
|
|
|
String scopeF = '123456789'; //首位
|
|
|
String scopeC = '0123456789'; //中间
|
|
|
String result = '';
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
if (i == 0) {
|
|
|
result = scopeF[Random().nextInt(scopeF.length)];
|
|
|
} else {
|
|
|
result = result + scopeC[Random().nextInt(scopeC.length)];
|
|
|
}
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
// sign=md5(ijddvzgEGaxbzsbmCtpdohxHyrAArwJB1003)
|
|
|
// =3967eaebec0eed0642a1d395ac9293dd
|
|
|
// String APPkey = 'ijddvzgEGaxbzsbmCtpdohxHyrAArwJB'; // 宜宾APPkey
|
|
|
//Flutter对字符串进行MD5运算 发表于 2019-03-26 更新于 2020-12-04 分类于 Flutter 阅读次
|
|
|
String GenerateMd5(String str) {
|
|
|
var content = new Utf8Encoder().convert(str);
|
|
|
var md5 = crypto.md5;
|
|
|
var digest = md5.convert(content);
|
|
|
return hex.encode(digest.bytes);
|
|
|
}
|
|
|
|
|
|
//加载中的圈圈
|
|
|
Widget getMoreWidget2({
|
|
|
Color color = Colors.white,
|
|
|
String text = '加载中...',
|
|
|
double size = 30.0,
|
|
|
double strokeWidth = 3.0,
|
|
|
FontWeight fontWeight,
|
|
|
double edge = 10.0,
|
|
|
double height = 34,
|
|
|
}) {
|
|
|
return Center(
|
|
|
child: Padding(
|
|
|
padding: EdgeInsets.all(edge),
|
|
|
child: Column(
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
children: <Widget>[
|
|
|
SizedBox(
|
|
|
height: size,
|
|
|
width: size,
|
|
|
child: CircularProgressIndicator(
|
|
|
strokeWidth: strokeWidth,
|
|
|
valueColor: AlwaysStoppedAnimation<Color>(color),
|
|
|
),
|
|
|
),
|
|
|
SizedBox(
|
|
|
height: ScreenUtil().setHeight(height),
|
|
|
),
|
|
|
Text(
|
|
|
text,
|
|
|
textAlign: TextAlign.center,
|
|
|
style: TextStyle(
|
|
|
fontSize: size, // 文字大小
|
|
|
color: color,
|
|
|
fontWeight: fontWeight, // 文字颜色
|
|
|
),
|
|
|
),
|
|
|
],
|
|
|
),
|
|
|
),
|
|
|
);
|
|
|
}
|
|
|
|
|
|
//加载中的圈圈
|
|
|
Widget getMoreWidget({
|
|
|
String text = '加载中...',
|
|
|
Color color = Colors.white,
|
|
|
double size = 30.0,
|
|
|
double strokeWidth = 3.0,
|
|
|
TextAlign textAlign = TextAlign.left,
|
|
|
}) {
|
|
|
return Center(
|
|
|
child: Padding(
|
|
|
padding: EdgeInsets.all(10.0),
|
|
|
child: Column(
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
children: <Widget>[
|
|
|
SizedBox(
|
|
|
height: size,
|
|
|
width: size,
|
|
|
child: CircularProgressIndicator(
|
|
|
strokeWidth: strokeWidth,
|
|
|
valueColor: AlwaysStoppedAnimation<Color>(color),
|
|
|
),
|
|
|
),
|
|
|
SizedBox(
|
|
|
height: 10,
|
|
|
),
|
|
|
Text(
|
|
|
text,
|
|
|
textAlign: textAlign,
|
|
|
style: TextStyle(
|
|
|
fontSize: size, // 文字大小
|
|
|
color: color,
|
|
|
),
|
|
|
),
|
|
|
],
|
|
|
),
|
|
|
),
|
|
|
);
|
|
|
}
|
|
|
|
|
|
//自定义带说明图标按钮函数。点击说明文字有反应
|
|
|
Widget getIconAndTextButton(
|
|
|
{IconData iconData, Color iconColor = Colors.black, var onPress = null}) {
|
|
|
return Container(
|
|
|
width: 50,
|
|
|
height: 50,
|
|
|
alignment: const Alignment(0, 0),
|
|
|
child: FlatButton(
|
|
|
padding: EdgeInsets.all(0),
|
|
|
onPressed: onPress,
|
|
|
//color: Colors.blue,
|
|
|
color: Colors.transparent,
|
|
|
//解决报错问题:FittedBox ← Expanded ← ConstrainedBox ← Container ← Center ← Padding ←
|
|
|
// Container ← IconTheme ← Builder ← _PointerListener
|
|
|
child: ConstrainedBox(
|
|
|
constraints: BoxConstraints(
|
|
|
maxHeight: 36, //最大高度
|
|
|
maxWidth: 36, //最大宽度
|
|
|
),
|
|
|
child: Padding(
|
|
|
padding: EdgeInsets.only(top: 1),
|
|
|
child: Icon(iconData, color: iconColor, size: 32),
|
|
|
// child: Image.asset(
|
|
|
// 'assets/images/left_arrow.png',
|
|
|
// fit: BoxFit.fitWidth,
|
|
|
// color: iconColor,
|
|
|
// //fit: BoxFit.cover,
|
|
|
// ),
|
|
|
),
|
|
|
|
|
|
// child: Row(
|
|
|
// mainAxisAlignment: MainAxisAlignment.center,
|
|
|
// crossAxisAlignment: CrossAxisAlignment.end,
|
|
|
// children: [
|
|
|
// Image.asset('assets/images/left_arrow.png', fit: BoxFit.cover),
|
|
|
// // Expanded(
|
|
|
// // //child: Icon(iconData, color: iconColor, size: 24),
|
|
|
// // child: Image.asset('assets/images/left_arrow.png', fit: BoxFit.cover),
|
|
|
// // ),
|
|
|
// ],
|
|
|
// ),
|
|
|
),
|
|
|
|
|
|
//The offending Expanded is currently placed inside a ConstrainedBox widget.
|
|
|
//The ownership chain for the RenderObject that received the incompatible parent data was:
|
|
|
// FittedBox ← Expanded ← ConstrainedBox ← Container ← Center ← Padding ← Container ← IconTheme ←
|
|
|
//Builder ← _PointerListener
|
|
|
|
|
|
//The ParentDataWidget Expanded(flex: 1) wants to apply ParentData of type FlexParentData to a
|
|
|
//RenderObject, which has been set up to accept ParentData of incompatible type ParentData.
|
|
|
//Usually, this means that the Expanded widget has the wrong ancestor RenderObjectWidget. Typically,
|
|
|
//Expanded widgets are placed directly inside Flex widgets.
|
|
|
//The offending Expanded is currently placed inside a FittedBox widget.
|
|
|
//The ownership chain for the RenderObject that received the incompatible parent data was:
|
|
|
// ConstrainedBox ← Container ← Expanded ← FittedBox ← Center ← Padding ← Container ← IconTheme ←
|
|
|
//Builder ← _PointerListener ← ⋯
|
|
|
// child: ConstrainedBox(
|
|
|
// constraints: BoxConstraints(
|
|
|
// maxHeight: 20, //最大高度
|
|
|
// maxWidth: 28, //最大宽度
|
|
|
// ),
|
|
|
// child: Container(
|
|
|
// child: Expanded(
|
|
|
// child: new Icon(iconData, color: iconColor),
|
|
|
// ),
|
|
|
// ),
|
|
|
// ),
|
|
|
|
|
|
// child: ConstrainedBox(
|
|
|
// constraints: BoxConstraints(
|
|
|
// maxHeight: 30, //最大高度
|
|
|
// maxWidth: 38, //最大宽度
|
|
|
// minWidth: 38, //最大宽度
|
|
|
// ),
|
|
|
// child: Row(
|
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
// children: [
|
|
|
// Expanded(
|
|
|
// child: Icon(iconData, color: iconColor, size: 24),
|
|
|
// ),
|
|
|
// ],
|
|
|
// ),
|
|
|
// ),
|
|
|
|
|
|
// child: Container(
|
|
|
// width: 28,
|
|
|
// height: 20,
|
|
|
// child: Expanded(
|
|
|
// child: new FittedBox(
|
|
|
// fit: BoxFit.fill,
|
|
|
// child: new Icon(iconData, color: iconColor),
|
|
|
// ),
|
|
|
// ),
|
|
|
// ),
|
|
|
|
|
|
// child: FittedBox(
|
|
|
// fit: BoxFit.fitWidth,
|
|
|
// alignment: Alignment.topLeft,
|
|
|
// child: Container(
|
|
|
// width: 28,
|
|
|
// height: 20,
|
|
|
// child: Expanded(
|
|
|
// child: new FittedBox(
|
|
|
// fit: BoxFit.fill,
|
|
|
// child: new Icon(iconData, color: iconColor),
|
|
|
// ),
|
|
|
// ),
|
|
|
// ),
|
|
|
// ),
|
|
|
),
|
|
|
);
|
|
|
}
|
|
|
|
|
|
///获取缩进空白符
|
|
|
String getDeepSpace(int deep) {
|
|
|
var tab = StringBuffer();
|
|
|
for (int i = 0; i < deep; i++) {
|
|
|
tab.write("\t");
|
|
|
}
|
|
|
return tab.toString();
|
|
|
}
|
|
|
|
|
|
// List map2list(Map _map) {
|
|
|
// List _list = [];
|
|
|
// _list = List.generate(listContacts2[widget.contactIndex].length, (index) {
|
|
|
// String key = listContacts2[widget.contactIndex].keys.elementAt(index);
|
|
|
// //return TextEditingController(text: listContacts2[widget.contactIndex][key]);
|
|
|
// return TextEditingController(text: getUserText3(widget.contactIndex, key));
|
|
|
// });
|
|
|
//
|
|
|
// }
|
|
|
|
|
|
/// [object] 解析的对象
|
|
|
/// [deep] 递归的深度,用来获取缩进的空白长度
|
|
|
/// [isObject] 用来区分当前map或list是不是来自某个字段,则不用显示缩进。单纯的map或list需要添加缩进
|
|
|
String json_print(dynamic object, int deep, {bool isObject = false}) {
|
|
|
var buffer = StringBuffer();
|
|
|
var nextDeep = deep + 1;
|
|
|
if (object is Map) {
|
|
|
var list = object.keys.toList();
|
|
|
if (!isObject) {
|
|
|
//如果map来自某个字段,则不需要显示缩进
|
|
|
buffer.write("${getDeepSpace(deep)}");
|
|
|
}
|
|
|
buffer.write("{");
|
|
|
if (list.isEmpty) {
|
|
|
//当map为空,直接返回‘}’
|
|
|
buffer.write("}");
|
|
|
} else {
|
|
|
buffer.write("\n");
|
|
|
for (int i = 0; i < list.length; i++) {
|
|
|
buffer.write("${getDeepSpace(nextDeep)}\"${list[i]}\":");
|
|
|
buffer.write(json_print(object[list[i]], nextDeep, isObject: true));
|
|
|
if (i < list.length - 1) {
|
|
|
buffer.write(",");
|
|
|
buffer.write("\n");
|
|
|
}
|
|
|
}
|
|
|
buffer.write("\n");
|
|
|
buffer.write("${getDeepSpace(deep)}}");
|
|
|
}
|
|
|
} else if (object is List) {
|
|
|
if (!isObject) {
|
|
|
//如果list来自某个字段,则不需要显示缩进
|
|
|
buffer.write("${getDeepSpace(deep)}");
|
|
|
}
|
|
|
buffer.write("[");
|
|
|
if (object.isEmpty) {
|
|
|
//当list为空,直接返回‘]’
|
|
|
buffer.write("]");
|
|
|
} else {
|
|
|
buffer.write("\n");
|
|
|
for (int i = 0; i < object.length; i++) {
|
|
|
buffer.write(json_print(object[i], nextDeep));
|
|
|
if (i < object.length - 1) {
|
|
|
buffer.write(",");
|
|
|
buffer.write("\n");
|
|
|
}
|
|
|
}
|
|
|
buffer.write("\n");
|
|
|
buffer.write("${getDeepSpace(deep)}]");
|
|
|
}
|
|
|
} else if (object is String) {
|
|
|
//为字符串时,需要添加双引号并返回当前内容
|
|
|
buffer.write("\"$object\"");
|
|
|
} else if (object is num || object is bool) {
|
|
|
//为数字或者布尔值时,返回当前内容
|
|
|
buffer.write(object);
|
|
|
} else {
|
|
|
//如果对象为空,则返回null字符串
|
|
|
buffer.write("null");
|
|
|
}
|
|
|
return buffer.toString();
|
|
|
}
|
|
|
|
|
|
//人脸识别和上传的图片,都需要转换为 Base64 格式的字符串提交
|
|
|
//通过图片路径将图片转换成 Base64 字符串
|
|
|
Future image2Base64(String imagePath) async {
|
|
|
File file = File(imagePath);
|
|
|
List<int> imageBytes = await file.readAsBytes();
|
|
|
String bs64 = base64Encode(imageBytes);
|
|
|
String ext = getFileExtension(imagePath);
|
|
|
print('imagePath = $imagePath, ext = $ext');
|
|
|
//String bs64Image = "data:image/png;base64," + bs64;
|
|
|
String bs64Image = "data:image/${ext};base64," + bs64;
|
|
|
return bs64Image;
|
|
|
}
|
|
|
|
|
|
//从字符路径 path 获取扩展名,不含点号
|
|
|
getFileExtension(String path) {
|
|
|
//return path.substring(path.lastIndexOf('.'));
|
|
|
//imagePath = /data/user/0/com.flutter.hyzp_ybqx/app_flutter/Pictures/flutter_test/1614662209478.jpg, ext = .jpg
|
|
|
return path.substring(path.lastIndexOf('.') + 1); //不含点号
|
|
|
}
|
|
|
|
|
|
//从字符路径 path 获取文件名(含扩展名)
|
|
|
getFileName(String path) {
|
|
|
//return path.substring(path.lastIndexOf('.'));
|
|
|
//imagePath = /data/user/0/com.flutter.hyzp_ybqx/app_flutter/Pictures/flutter_test/1614662209478.jpg, ext = .jpg
|
|
|
return path.substring(path.lastIndexOf('/') + 1); //不含点号
|
|
|
}
|
|
|
|
|
|
Widget getBtnSizeX({@required text, double width = 70.0, double height = 40.0, onPressedFun}) {
|
|
|
return Container(
|
|
|
color: Colors.white12, //onPressedFun为null时无效
|
|
|
width: width,
|
|
|
height: height,
|
|
|
child: RaisedButton(
|
|
|
padding: EdgeInsets.all(0),
|
|
|
textColor: Colors.black,
|
|
|
child: Text(text),
|
|
|
onPressed: onPressedFun,
|
|
|
),
|
|
|
);
|
|
|
}
|
|
|
|
|
|
//返回主页ashamp
|
|
|
// 博客园首页新随笔联系管理订阅订阅随笔 - 33 文章 - 0 评论 - 51 阅读 - 55018
|
|
|
// 在debug时使Flutter中的print打印json数据时更美观易读
|
|
|
// 为了避免deubg信息在生产环境打印,只在测试时打印,在main函数中,改变debugPrint的指向
|
|
|
//
|
|
|
// 复制代码
|
|
|
// main(){
|
|
|
// if (Api.isDebug) {
|
|
|
// debugPrint = (String message, {int wrapWidth}) {
|
|
|
// try {
|
|
|
// var object = json.decode(message);
|
|
|
// message = JsonEncoder.withIndent(' ').convert(object);
|
|
|
// } catch (e) {}
|
|
|
// printWrapped(message);
|
|
|
// };
|
|
|
// } else {
|
|
|
// debugPrint = (String message, {int wrapWidth}) {};
|
|
|
// }
|
|
|
// }
|
|
|
// 复制代码
|
|
|
// 将printWrapped方法放入工具类或你需要的地方
|
|
|
//
|
|
|
// void printWrapped(String text) {
|
|
|
// final pattern = new RegExp('.{1,800}'); // 800 is the size of each chunk
|
|
|
// pattern.allMatches(text).forEach((match) => developer.log(match.group(0)));
|
|
|
// }
|
|
|
// log方法需要引入
|
|
|
//
|
|
|
// import 'dart:developer' as developer;
|
|
|
//
|
|
|
|
|
|
//在debug时使 Flutter 中的 print 打印 json 数据时更美观易读
|
|
|
void jsonPrint(String message, {int len = 800}) {
|
|
|
//是否在生产环境
|
|
|
const bool isDebug = !const bool.fromEnvironment("dart.vm.product");
|
|
|
// if (!isDebug) {
|
|
|
// return;
|
|
|
// }
|
|
|
|
|
|
try {
|
|
|
var object = json.decode(message);
|
|
|
message = JsonEncoder.withIndent(' ').convert(object);
|
|
|
} catch (e) {
|
|
|
print('e = $e');
|
|
|
}
|
|
|
printWrapped(message);
|
|
|
}
|
|
|
|
|
|
// 打印长字符串
|
|
|
void printWrapped(String text, {int len = 800}) {
|
|
|
final pattern = RegExp('.{1,$len}'); // 800 is the size of each chunk
|
|
|
pattern.allMatches(text).forEach((match) => developer.log(match.group(0)));
|
|
|
}
|
|
|
|
|
|
//OK
|
|
|
void my_segmentPrint(String str, {int len = 800}) {
|
|
|
//是否在生产环境
|
|
|
const bool isDebug = !const bool.fromEnvironment("dart.vm.product");
|
|
|
if (!isDebug) {
|
|
|
return;
|
|
|
}
|
|
|
List list = strToList(str);
|
|
|
for (int i = 0; i < list.length; i++) {
|
|
|
print('${list[i]}');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//OK
|
|
|
List strToList(String str, {int len = 800}) {
|
|
|
List<String> strList = [];
|
|
|
if (str.length <= len) {
|
|
|
strList.add(str);
|
|
|
} else {
|
|
|
int splitCount = str.length ~/ len; //应该切割的次数
|
|
|
for (int i = 0; i < splitCount; i++) {
|
|
|
strList.add(str.substring(len * i, len * (i + 1)));
|
|
|
}
|
|
|
|
|
|
//处理最后一段
|
|
|
if (str.length % len != 0) {
|
|
|
strList.add(str.substring(len * splitCount));
|
|
|
}
|
|
|
}
|
|
|
print('strList:${strList.toString}');
|
|
|
return strList;
|
|
|
}
|
|
|
|
|
|
Widget getImageWidget() {
|
|
|
return Container(
|
|
|
alignment: Alignment(0, 0),
|
|
|
height: ScreenUtil().setHeight(346),
|
|
|
width: ScreenUtil().setWidth(942),
|
|
|
decoration: BoxDecoration(
|
|
|
image: DecorationImage(image: AssetImage('assets/images/装饰图片10.png'), fit: BoxFit.cover),
|
|
|
),
|
|
|
child: Column(
|
|
|
children: [
|
|
|
Container(
|
|
|
alignment: Alignment.centerLeft,
|
|
|
padding: EdgeInsets.only(top: ScreenUtil().setWidth(30), left: ScreenUtil().setWidth(55)),
|
|
|
child: Text('改善城市空气质量', style: TextStyle(fontSize: 17, color: Colors.white)),
|
|
|
),
|
|
|
Container(
|
|
|
alignment: Alignment.centerLeft,
|
|
|
padding: EdgeInsets.only(left: ScreenUtil().setWidth(55)),
|
|
|
child: Text('建设长江生态第一城',
|
|
|
style: TextStyle(
|
|
|
fontSize: 19,
|
|
|
color: Color.fromRGBO(49, 216, 123, 1),
|
|
|
fontWeight: FontWeight.bold)),
|
|
|
),
|
|
|
SizedBox(height: ScreenUtil().setHeight(copyright_info.contains('\n') ? 50 : 90)),
|
|
|
Container(
|
|
|
alignment: Alignment.center,
|
|
|
child: Text(copyright_info,
|
|
|
style: TextStyle(fontSize: 13, color: Color.fromRGBO(192, 192, 192, 1)),
|
|
|
textAlign: TextAlign.center),
|
|
|
),
|
|
|
],
|
|
|
),
|
|
|
// child: Image.asset(
|
|
|
// 'assets/images/装饰图片10.png',
|
|
|
// fit: BoxFit.cover,
|
|
|
// ),
|
|
|
);
|
|
|
}
|
|
|
|
|
|
Widget getIconBtnSizeX(
|
|
|
{@required text,
|
|
|
width = 233,
|
|
|
height = 116,
|
|
|
onTop,
|
|
|
Color color = Colors.black,
|
|
|
double circular = 10,
|
|
|
double textSize = 18}) {
|
|
|
return InkWell(
|
|
|
onTap: onTop,
|
|
|
child: Container(
|
|
|
alignment: Alignment(0, 0),
|
|
|
margin: EdgeInsets.all(0),
|
|
|
padding: EdgeInsets.all(0),
|
|
|
width: ScreenUtil().setWidth(width),
|
|
|
height: ScreenUtil().setHeight(height),
|
|
|
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(circular)),
|
|
|
child: Row(
|
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
|
children: [
|
|
|
SizedBox(width: ScreenUtil().setWidth(12)),
|
|
|
Icon(Icons.play_arrow, color: Colors.white, size: ScreenUtil().setWidth(56)),
|
|
|
Text(
|
|
|
text,
|
|
|
style: TextStyle(color: Colors.white, fontSize: textSize),
|
|
|
)
|
|
|
],
|
|
|
),
|
|
|
),
|
|
|
);
|
|
|
}
|