瞄准器,地图

This commit is contained in:
tanlinxing 2024-08-17 18:09:43 +08:00
parent 2676be5902
commit 4ca6668e11
189 changed files with 11128 additions and 823 deletions

BIN
images/navi_pointer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
images/satellite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

162
lib/appbar/appbar.dart Normal file
View File

@ -0,0 +1,162 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../login_in/connect/bluetooth_page.dart';
import '../login_in/connect/connect_type.dart';
import '../login_in/getx/real_data.dart';
import '../main.dart';
import '../screens/aimpoint_page.dart';
final RealController controller1 = Get.find();
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
final double appBarHeight;
final RxInt _currentIndex;
CustomAppBar({required this.appBarHeight, required RxInt currentIndex})
: _currentIndex = currentIndex;
@override
Widget build(BuildContext context) {
const textStyle = TextStyle(fontSize: 16);
return AppBar(
toolbarHeight: 40,
centerTitle: true, //
title: Obx(() {
if (_currentIndex.value == 0) {
return const Text("桩点:", style: textStyle);
} else if (_currentIndex.value == 1) {
return const Text("设备:", style: textStyle);
} else {
return const Text("系统:", style: textStyle);
}
}),
// title: _currentIndex.value == 0
// ? Obx(() => Text("桩点:${realController.pileId.value}",
// style: textStyle))
// : const Text("设备:", style: textStyle),
// // const Center(
// // child: Obx(() => Text("设备:", style: textStyle)),
// // ),
// title: Obx(() {
// if (_currentIndex.value == 1) {
// return const Text("设备:", style: textStyle);
// } else if (_currentIndex.value == 0) {
// return Text("桩点:${realController.pileId.value}",
// style: textStyle);
// } else {
// return const Text("系统:", style: textStyle);
// }
// }),
actions: [
InkWell(
onTap: () {
MyApp.isDarkMode.value = !MyApp.isDarkMode.value;
},
child: Icon(
MyApp.isDarkMode.value ? Icons.dark_mode : Icons.sunny,
size: 35,
),
),
const SizedBox(
width: 10,
),
InkWell(
onTap: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => const ScenceMap())),
child: Image(
image: const AssetImage('images/satellite.png'),
// width: 40,
color: MyApp.isDarkMode.value
? Colors.white70
: const Color.fromARGB(200, 29, 28, 28),
height: 40,
),
),
const SizedBox(
width: 10,
),
_currentIndex.value == 1
? Row(
children: [
Obx(
() => InkWell(
onTap: () {
//
sight.isCardVisible.value =
!sight.isCardVisible.value; //
},
child: Icon(
Icons.my_location_sharp,
size: 35,
color: sight.isCardVisible.value
? Colors.blue
// : const Color.fromARGB(200, 29, 28, 28),
: (MyApp.isDarkMode.value
? Colors.white70
: const Color.fromARGB(200, 29, 28, 28)),
), //
),
),
const SizedBox(
width: 10,
),
Obx(
() => InkWell(
onTap: () {
final RealController controller1 = Get.find(); //
controller1.isDataVisible.value =
!controller1.isDataVisible.value; //
},
child: Icon(
Icons.date_range_rounded,
size: 35,
color: controller1.isDataVisible.value
? Colors.blue
: (MyApp.isDarkMode.value
? Colors.white70
: const Color.fromARGB(200, 29, 28, 28)),
), //
),
),
const SizedBox(
width: 10,
),
UnconstrainedBox(
child: SizedBox(
height: 30,
child: Builder(
builder: (context) => InkWell(
child: Icon(
Icons.settings_outlined,
size: 35,
color: MyApp.isDarkMode.value
? Colors.white70
: const Color.fromARGB(200, 29, 28, 28),
),
onTap: () => Scaffold.of(context).openEndDrawer(),
),
),
),
)
],
)
: const Text(""),
Obx(() => Icon(
blueToothController.connectedType.value == ConnectType.wifi
? Icons.wifi
: (blueToothController.connectedType.value ==
ConnectType.bluetooth
? Icons.bluetooth
: Icons.close),
color: Colors.green,
size: 30,
))
],
);
}
@override
Size get preferredSize => Size.fromHeight(appBarHeight);
}

View File

@ -0,0 +1,59 @@
// ignore_for_file: constant_identifier_names
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:quick_blue/quick_blue.dart';
import 'bluetooth_page.dart';
import 'connect_type.dart';
class BlueParams {
String serviceId = "00FF";
//
// Map<String, String> characteristicId = {
// "coorTransId": "FF01", //
// "realId": "FF02", //
// };
String characteristicId = "FF01";
String gssUuid(String code) => '0000$code-0000-1000-8000-00805f9b34fb';
}
class BlueSetting {
BlueParams blueParams = BlueParams();
connect(String deviceId) async {
blueToothController.connectedType.value = ConnectType.bluetooth;
blueToothController.connectedDeviceID.value = deviceId;
blueToothController.isConnected.value = true;
QuickBlue.connect(deviceId);
QuickBlue.discoverServices(deviceId);
String service = blueParams.gssUuid(blueParams.serviceId);
String characteristic = blueParams.gssUuid("FF01");
//
Future.delayed(const Duration(milliseconds: 2000), () {
QuickBlue.writeValue(deviceId, service, characteristic,
Uint8List.fromList([0xf2]), BleOutputProperty.withResponse);
});
}
disConnect(deviceId) {
blueToothController.isConnected.value = false;
QuickBlue.disconnect(deviceId);
}
write(String deviceId, Uint8List bytes) {
QuickBlue.writeValue(
deviceId,
blueParams.gssUuid(blueParams.serviceId),
blueParams.gssUuid(blueParams.characteristicId),
bytes,
BleOutputProperty.withResponse);
}
}
enum Coord {
BJ54,
XA80,
WGS84,
WGS2000,
}

View File

@ -0,0 +1,344 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:get/get.dart';
import 'package:open_settings/open_settings.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:quick_blue/quick_blue.dart';
import 'blue_tooth.dart';
import 'config.dart';
import '../getx/blue_tooth.dart';
import '../getx/real_data.dart';
BlueParams blueParams = BlueParams();
BlueSetting blueSetting = BlueSetting();
//
TextStyle textStyle = const TextStyle(fontSize: 15);
//
final _scanResults = <BlueScanResult>[];
//
bool isBlueToothOpen = false;
//
int connetedIndex = -1;
//id
String selectDeviceId = "";
//
bool isFirstAsk = true;
final BlueToothController blueToothController = Get.put(BlueToothController());
class BlueTooth extends StatefulWidget {
const BlueTooth({super.key});
@override
State<BlueTooth> createState() => _BlueToothState();
}
class _BlueToothState extends State<BlueTooth> {
StreamSubscription<BlueScanResult>? _scanResultSubscription;
// StreamSubscription<AvailabilityState>? _availabilitySubscription;
Widget _buildListView(BuildContext context) {
return ListView.separated(
itemBuilder: (context, index) {
String text = "连接";
if (_scanResults[index].deviceId == selectDeviceId) {
text = "断开连接";
}
return Container(
height: 50,
margin: const EdgeInsets.only(bottom: 10),
// decoration:
// BoxDecoration(color: isDarkMode ? Colors.black : Colors.white),
child: Row(
children: [
Expanded(
child: ListTile(
title: Text(
'${_scanResults[index].name}(${_scanResults[index].rssi})'), //rssi信号强度指示
subtitle: Text(_scanResults[index].deviceId),
onTap: () async {
setState(() {
connetedIndex = index;
selectDeviceId = _scanResults[connetedIndex].deviceId;
AppConfig.updateConfig("deviceId", selectDeviceId);
});
bool tt = await QuickBlue.isBluetoothAvailable();
if (tt) {
blueSetting.connect(selectDeviceId);
} else {
setState(() {
settingBluetoothDialog();
});
}
},
),
),
Container(
padding: const EdgeInsets.only(right: 5),
// width: 80,
child: ElevatedButton(
onPressed: () async {
if (text == "断开连接") {
QuickBlue.disconnect(selectDeviceId);
selectDeviceId = "";
blueToothController.connectedDeviceID.value = "";
// blueToothController.connectedType.value = "";
setState(() {
text = "连接";
});
} else {
setState(() {
if (_scanResults.isNotEmpty) {
connetedIndex = index;
selectDeviceId = _scanResults[connetedIndex].deviceId;
AppConfig.updateConfig("deviceId", selectDeviceId);
}
});
bool tt = await QuickBlue.isBluetoothAvailable();
if (tt) {
blueSetting.connect(selectDeviceId);
} else {
setState(() {
settingBluetoothDialog();
});
}
}
},
child: Text(
text,
style: textStyle,
),
),
)
],
),
);
},
separatorBuilder: (context, index) => const Divider(),
itemCount: _scanResults.length,
);
}
void startBluetoothScan() {
_scanResults.length = 0;
//
QuickBlue.availabilityChangeStream.listen((AvailabilityState state) {
debugPrint('Bluetooth state: ${state.toString()}');
});
//
_scanResultSubscription =
QuickBlue.scanResultStream.listen((BlueScanResult result) {
if (!_scanResults.any((r) => r.deviceId == result.deviceId)) {
setState(() {
_scanResults.add(result);
if (result.deviceId == blueToothController.connectedDeviceID.value) {
connetedIndex = _scanResults.length - 1;
selectDeviceId = result.deviceId;
blueSetting.connect(selectDeviceId);
}
});
}
});
//
QuickBlue.startScan();
QuickBlue.setConnectionHandler(_handleConnectionChange);
QuickBlue.setServiceHandler(_handleServiceDiscovery);
QuickBlue.setValueHandler(_handleValueChange);
}
Future<void> openBluetoothDialog() {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("提示"),
content: const Text("无法获取到开启蓝牙和位置权限,请到系统设置中开启蓝牙和位置权限。"),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("确定"))
]);
});
}
Future<void> settingBluetoothDialog() {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("提示"),
content: SingleChildScrollView(
child: ListBody(
children: [
const Center(
child: Text("蓝牙暂未开启"),
),
const SizedBox(
height: 20,
),
TextButton(
onPressed: () {
OpenSettings.openBluetoothSetting();
},
child: const Text("去打开"))
],
),
),
actions: <Widget>[
TextButton(
child: const Text("确定"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
//
PermissionStatus denied = PermissionStatus.denied;
//
PermissionStatus granted = PermissionStatus.granted;
final RealController realController = Get.put(RealController());
Uint8List bleList = Uint8List(0);
@override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) async {
//
if (await QuickBlue.isBluetoothAvailable() == true) {
//
if (await Permission.location.status == granted &&
await Permission.bluetoothScan.status == granted &&
await Permission.bluetoothConnect.status == granted) {
//
startBluetoothScan();
} else {
if (isFirstAsk) {
if (await Permission.location.status == denied) {
await Permission.location.request();
}
if (await Permission.bluetoothScan.status == denied) {
await Permission.bluetoothScan.request();
}
if (await Permission.bluetoothConnect.status == denied) {
await Permission.bluetoothConnect.request();
}
isFirstAsk = false;
if (await Permission.location.status == granted &&
await Permission.bluetoothScan.status == granted &&
await Permission.bluetoothConnect.status == granted) {
startBluetoothScan();
} else {
setState(() {
openBluetoothDialog();
});
}
} else {
setState(() {
openBluetoothDialog();
});
}
}
} else {
setState(() {
settingBluetoothDialog();
});
}
});
}
void _handleConnectionChange(String deviceId, BlueConnectionState state) {
if (state == BlueConnectionState.connected) {
QuickBlue.discoverServices(selectDeviceId);
}
if (state == BlueConnectionState.connected) {
QuickBlue.discoverServices(selectDeviceId);
//
// //id
// settingController.changeID(bluetoothId);
} else if (state == BlueConnectionState.disconnected) {
// blueToothController.connectedType.value = "";
}
}
void _handleServiceDiscovery(
String deviceId, String serviceId, List<String> characteristicIds) {
if (characteristicIds.contains(blueParams.gssUuid("FF02").toLowerCase())) {
QuickBlue.setNotifiable(selectDeviceId, serviceId,
blueParams.gssUuid("FF02"), BleInputProperty.indication);
}
print('_handleServiceDiscovery $deviceId, $serviceId, $characteristicIds');
}
List<Uint8List> originalList = [];
void _handleValueChange(
String deviceId, String characteristicId, Uint8List value) {
ByteData byteData = value.buffer.asByteData();
int dataType = byteData.getInt8(2);
originalList.add(value);
if (dataType == 4) {
cropAndConcatUint8List(originalList);
Uint8List list = cropAndConcatUint8List(originalList);
realController.onBleData(list);
originalList.length = 0;
}
//
}
Uint8List cropAndConcatUint8List(List<Uint8List> originalList) {
List<int> concatenatedList = [];
for (int i = 0; i < originalList.length; i++) {
Uint8List currentUint8List = originalList[i];
if (currentUint8List.length >= 3) {
List<int> croppedList = currentUint8List.sublist(3);
concatenatedList.addAll(croppedList);
}
}
return Uint8List.fromList(concatenatedList);
}
@override
void dispose() {
super.dispose();
QuickBlue.stopScan();
// QuickBlue.setValueHandler(null);
// QuickBlue.setServiceHandler(null);
// QuickBlue.setConnectionHandler(null);
_scanResultSubscription?.cancel();
// _availabilitySubscription?.cancel();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("蓝牙设备"),
actions: [
IconButton(
onPressed: () {
startBluetoothScan();
},
icon: const Icon(Icons.refresh))
],
),
body: _buildListView(context),
);
}
}

View File

@ -0,0 +1,69 @@
import 'dart:convert';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'connect_type.dart';
class AppConfig {
static late final Map<String, dynamic> config;
static Future<void> load() async {
String configString = await _readConfigFile();
if (configString.isEmpty) {
config = {};
return;
}
config = json.decode(configString);
}
static Future<String> _readConfigFile() async {
Directory directory = await getApplicationDocumentsDirectory();
File file = File('${directory.path}/config.json');
if (await file.exists()) {
} else {
Map content = {};
String fileContent = json.encode(content);
file.writeAsStringSync(fileContent);
}
return await file.readAsString();
}
static Future<void> _writeConfigFile(String content) async {
Directory directory = await getApplicationDocumentsDirectory();
File file = File('${directory.path}//config.json');
await file.writeAsString(content);
}
static void updateConfig(String key, dynamic value) {
config[key] = value;
_writeConfigFile(json.encode(config));
}
}
// config
class Config {
// wifi
ConnectType? connectType;
// id
String? deviceId;
// wifi
String? ip;
int? port;
Config({
this.connectType,
this.deviceId,
this.ip,
this.port,
});
factory Config.fromJson(Map<dynamic, dynamic> data) {
return Config(
connectType: data['connectType'] == "wifi"
? ConnectType.wifi
: ConnectType.bluetooth,
deviceId: data['deviceId'] ?? "",
ip: data['ip'] ?? "192.168.4.1",
port: data['port'] == null ? 6000 : data["port"],
);
}
}

View File

@ -0,0 +1,6 @@
enum ConnectType {
wifi,
bluetooth,
none
}

View File

@ -0,0 +1,184 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import '../service/coor_trans.dart';
import 'bluetooth_page.dart';
import 'config.dart';
import 'connect_type.dart';
import 'blue_tooth.dart';
import 'wifi.dart';
class Connect {
SocketSetting socket = SocketSetting();
BlueSetting blueSetting = BlueSetting();
Config config = Config();
init() async {
await AppConfig.load();
config = Config.fromJson(AppConfig.config);
if (config.connectType != null) {
// if (config.connectType == "wifi") {
// config.connectType = ConnectType.wifi;
// } else {
// config.connectType = ConnectType.bluetooth;
// }
blueToothController.connectedType.value =
config.connectType ?? ConnectType.none;
if (config.connectType == ConnectType.wifi) {
if (config.ip != null) {
socket.ip = config.ip ?? "192.168.4.1";
socket.port = config.port ?? 6000;
await socket.connect();
}
} else {
if (config.deviceId != "") {
blueToothController.connectedDeviceID.value = config.deviceId!;
//
await blueSetting.connect(config.deviceId!);
}
}
}
}
disconnect(ConnectType type) {
if (type == ConnectType.wifi) {
socket.disConnect();
} else {
blueSetting.disConnect(blueToothController.connectedDeviceID.value);
}
}
//
write(Uint8List value) {
if (blueToothController.connectedType.value == ConnectType.wifi) {
socket.write(value);
} else if (blueToothController.connectedType.value ==
ConnectType.bluetooth) {
blueSetting.write(blueToothController.connectedDeviceID.value, value);
} else {
print("暂无连接");
}
}
writeCoorTransValue(String deviceId, CoorTransModel coorTrans) {
Uint8List bytes = Uint8List(20);
ByteData data = ByteData.view(bytes.buffer);
// 0xF0 + 2 + 1+
data.setInt8(0, 0xF0);
data.setInt16(1, 608, Endian.little);
data.setInt8(3, 16);
// data.setFloat64(4, coorTrans.L0);
// 601 2
sendData(
bytes, 601, 2, [toCoordString(coorTrans.dstEllipsoid).index], deviceId);
// L0,elevation 608 16
Future.delayed(const Duration(milliseconds: 2000), () {
sendData(
bytes,
608,
16,
[(coorTrans.L0 * 1e8).toInt(), (coorTrans.elevation * 1000).toInt()],
deviceId);
});
// x 616 16
Future.delayed(const Duration(milliseconds: 3000), () {
sendData(
bytes,
616,
16,
[(coorTrans.dx * 1000).toInt(), (coorTrans.wx * 1000).toInt()],
deviceId);
});
// y 624 16
Future.delayed(const Duration(milliseconds: 4000), () {
sendData(
bytes,
624,
16,
[(coorTrans.dy * 1000).toInt(), (coorTrans.wy * 1000).toInt()],
deviceId);
});
// z 632 16
Future.delayed(const Duration(milliseconds: 4000), () {
sendData(
bytes,
632,
16,
[(coorTrans.dz * 1000).toInt(), (coorTrans.wz * 1000).toInt()],
deviceId);
});
// k 640 8
Future.delayed(const Duration(milliseconds: 5000), () {
ByteData data = ByteData.view(bytes.buffer);
data.setInt8(0, 0xF0);
data.setInt16(1, 640, Endian.little);
data.setInt8(3, 8);
data.setInt64(4, (coorTrans.k * 1000).toInt(), Endian.little);
write(bytes);
});
Future.delayed(const Duration(milliseconds: 7000), () {
save(deviceId, bytes, data);
});
}
void sendData(Uint8List bytes, int code, int length, List<int> values,
String deviceId) {
ByteData data = ByteData.view(bytes.buffer);
data.setInt8(0, 0xF0);
data.setInt16(1, code, Endian.little);
data.setInt8(3, length);
for (int i = 0; i < values.length; i++) {
data.setInt64(4 + i * 8, (values[i] * 1000).toInt(), Endian.little);
}
write(bytes);
}
save(String deviceId, Uint8List bytes, ByteData data) {
// 0X55BB 600 2
data.setInt16(1, 600, Endian.little);
data.setInt8(3, 2);
data.setUint16(4, 0x55AA, Endian.little);
write(bytes);
}
Coord toCoordString(String coord) {
switch (coord) {
case "BJ54":
return Coord.BJ54;
case "XA80":
return Coord.XA80;
case "WGS84":
return Coord.WGS84;
case "WGS2000":
return Coord.WGS2000;
default:
return Coord.WGS2000;
}
}
writeAntenna(Offset center, Offset right, String deviceId) {
Uint8List bytes = Uint8List(20);
ByteData data = ByteData.view(bytes.buffer);
// 604 8 X0右侧天线 X1
data.setInt8(0, 0xF0);
data.setInt16(1, 604, Endian.little);
data.setInt8(3, 8);
data.setInt16(4, (right.dx * 1000).toInt(), Endian.little);
data.setInt16(6, (right.dy * 1000).toInt(), Endian.little);
data.setInt16(8, (center.dx * 1000).toInt(), Endian.little);
data.setInt16(10, (center.dy * 1000).toInt(), Endian.little);
write(bytes);
Future.delayed(const Duration(milliseconds: 1000), () {
save(deviceId, bytes, data);
});
}
}

View File

@ -0,0 +1,49 @@
//
import 'package:flutter/material.dart';
import '../../main.dart';
import '../../screens/login_page.dart';
import '../user/loginprefs.dart';
/*
*
*/
Route<dynamic>? onGenerateRoute(RouteSettings settings) {
Map<String, Widget> routes = {
'home': const MyApp(), //app路径
'login': const Login(), //login路径
};
String routerName = routeBeforeHook(settings);
bool mathMap = false;
Route<dynamic>? mathWidget;
routes.forEach((key, v) {
if (key == routerName) {
mathMap = true;
mathWidget = MaterialPageRoute(builder: (BuildContext context) => v);
}
});
if (mathMap) {
return mathWidget;
}
return MaterialPageRoute(
builder: (BuildContext context) => const Text('404'));
}
String routeBeforeHook(RouteSettings settings) {
if (checkToken() == false) {
return 'login';
} else {
return settings.name!;
}
}
bool checkToken() {
LoginPrefs loginPrefs = LoginPrefs();
String token = loginPrefs.getToken();
if ('' != token) return true;
return false;
}

View File

@ -0,0 +1,92 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:get/get.dart';
import 'bluetooth_page.dart';
import '../getx/real_data.dart';
final RealController realController = Get.find();
class SocketSetting {
int port = 6000;
String ip = "192.168.4.1";
bool isConnected = false;
int times = 0;
RawDatagramSocket? socket;
connect() async {
// tcp连接
// try {
// socket = await Socket.connect(ip, port);
// //
// socket!.add([0xf2]);
// //
// socket!.listen(
// (Uint8List data) {
// realController.onBleData(data);
// // print('收到服务器回复:$data');
// },
// onError: (error) async {
// disConnect();
// print('与服务器的连接发生错误:$error');
// socket = await Socket.connect(ip, port);
// return;
// },
// );
// //
// socket!.done.then((_) {
// print('与服务器的连接已断开');
// disConnect();
// });
// } catch (e) {
// print("----------$e");
// disConnect();
// }
// udp连接
socket = await RawDatagramSocket.bind(
InternetAddress.anyIPv4, 6000); //InternetAddress(ip)
// //
socket!.send([0xf2], InternetAddress(ip), 6000);
socket!.listen((RawSocketEvent e) {
times++;
if (times == 20) {
times = 0;
socket!.send([0xf2], InternetAddress(ip), 6000);
}
Datagram? dg = socket!.receive();
if (dg != null) {
realController.onBleData(dg.data);
// print('${dg.address.address}:${dg.port} 接收:');
//
// socket.send("已收到".codeUnits, dg.address, dg.port);
}
});
}
write(Uint8List data) async {
try {
// socket ??= await Socket.connect(ip, port);
Datagram? dg = socket!.receive();
socket!.send(data, dg!.address, dg.port);
// socket!.add(data);
// ignore: empty_catches
} catch (e) {}
}
disConnect() async {
blueToothController.isConnectedWifi.value = false;
try {
socket!.close();
// await socket!.close();
} catch (e) {
//
}
}
}

View File

@ -0,0 +1,12 @@
import 'package:get/get.dart';
import '../connect/connect_type.dart';
class BlueToothController extends GetxController {
//
var connectedType = ConnectType.none.obs; // wifi
//id
RxString connectedDeviceID = "".obs;
var isConnected = false.obs;
var isConnectedWifi = false.obs;
}

View File

@ -0,0 +1,17 @@
import 'package:get/get.dart';
import 'package:scence_map/controller.dart';
import 'real_data.dart';
class GexRegister {
GexRegister();
void registerDependencies() {
Get.lazyPut(() => ScenceMapController());
Get.lazyPut(() => RealController());
}
}

View File

@ -0,0 +1,155 @@
import 'dart:typed_data';
import 'dart:ui';
import 'package:get/get.dart';
import '../../map_pointer/service/process.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
class RealController extends GetxController {
var sightOffset1 = const Offset(3, 3).obs;
var sightOffsetInit1 = const Offset(0, 0).obs;
var sightInit1 = const Offset(0, 0).obs;
var initDx = 0.0.obs;
reset() {
sightOffset1.value = const Offset(3, 3);
sightOffsetInit1.value = const Offset(0, 0);
sightInit1.value = const Offset(0, 0);
initDx.value = 0.0;
update();
}
RTCPeerConnection? peerConnection;
RTCDataChannel? dataChannel;
final speed = 0.0.obs;
final depth = 0.0.obs;
final tiltX = 0.0.obs;
final tiltY = 0.0.obs;
final centerX = 0.0.obs;
final centerY = 0.0.obs;
final current1 = 0.0.obs;
final current2 = 0.0.obs;
final totalFlow2 = 0.0.obs;
final totalFlow1 = 0.0.obs;
final flow10cm1 = 0.0.obs;
final flow10cm2 = 0.0.obs;
final subtotalFlow2 = 0.0.obs;
final subtotalFlow1 = 0.0.obs;
final recvTime = ''.obs;
final alt = 0.0.obs;
final lng = 0.0.obs;
final lat = 0.0.obs;
final pileId = ''.obs;
final time = "0".obs;
final startIndex = 0.obs;
// 线
final processList = <ProcessEntity>[].obs;
var isDataVisible = false.obs;
void updateProcessList(List<ProcessEntity> newList) {
processList.assignAll(newList); // 使 assignAll RxList
}
DateTime? sliderTime;
updateSlider(double newValue, [isHand = true]) {
if (isHand) {
sliderTime = DateTime.now();
}
startIndex.value = newValue.toInt();
update();
}
@override
void onInit() {
super.onInit();
//
// Timer.periodic(const Duration(milliseconds: 1000), (_) {
// time.value = DateTime.now();
// });
initializePeerConnection();
}
void initializePeerConnection() async {
final configuration = <String, dynamic>{
'iceServers': [
{'url': 'stun:stun.l.google.com:19302'},
],
};
peerConnection = await createPeerConnection(configuration);
initializeDataChannel();
}
void initializeDataChannel() async {
//
dataChannel = await peerConnection!
.createDataChannel('dataChannel', RTCDataChannelInit()..id = 1);
//
dataChannel!.onMessage = (RTCDataChannelMessage message) {
//
if (message.isBinary) {
onBleData(message.binary);
}
};
}
onBleData(Uint8List data) {
ByteData byteData = data.buffer.asByteData();
int tag = byteData.getUint8(0);
// int length = byteData.getUint8(1);
if (tag == 161) {
speed.value = byteData.getInt16(2, Endian.little) * 0.1;
int timeInt = byteData.getUint16(4, Endian.little);
// print(timeInt);
time.value = convertSecondsToMinSec(timeInt);
depth.value = byteData.getInt16(6, Endian.little) * 0.001;
pileId.value = byteData.getUint16(8, Endian.little).toString();
subtotalFlow1.value = byteData.getInt16(10, Endian.little) * 0.01;
flow10cm1.value = byteData.getUint16(12, Endian.little) * 0.01;
totalFlow1.value = byteData.getInt32(14, Endian.little) * 0.01;
subtotalFlow2.value = byteData.getInt16(18, Endian.little) * 0.01;
flow10cm2.value = byteData.getUint16(20, Endian.little) * 0.01;
totalFlow2.value = byteData.getInt32(22, Endian.little) * 0.01;
current1.value = byteData.getUint16(26, Endian.little) * 0.01;
current2.value = byteData.getUint16(28, Endian.little) * 0.01;
// current3.value = byteData.getUint16(30, Endian.little) * 0.01;
int tag1 = byteData.getUint8(34);
// int length = byteData.getUint8(35);
if (tag1 == 162) {
// var gpsStatus = byteData.getUint8(36);
// var gpsView = byteData.getUint8(37);
// var gpsUse = byteData.getUint8(38);
// var reg = byteData.getUint8(39);
// var utc = byteData.getUint32(40, Endian.little);
//
centerX.value = byteData.getFloat64(44, Endian.little);
centerY.value = byteData.getFloat64(52, Endian.little);
// print("中心---$centerX,$centerY");
// tiltX.value = byteData.getFloat64(44, Endian.little);
// tiltY.value = byteData.getFloat64(52, Endian.little);
// var direction = byteData.getFloat64(60, Endian.little);
// var pitch = byteData.getFloat64(68, Endian.little);
// print("$gpsUse,$gpsView,$utc,$direction,$pitch");
}
}
}
String convertSecondsToMinSec(int totalSeconds) {
int minutes = totalSeconds ~/ 60;
int seconds = totalSeconds % 60;
String formattedTime = '$minutes:${seconds.toString().padLeft(2, '0')}';
return formattedTime;
}
@override
void dispose() {
dataChannel?.close(); //
peerConnection?.close(); //
super.dispose();
}
}

View File

@ -0,0 +1,54 @@
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import '../user/loginprefs.dart';
LoginPrefs loginPrefs = LoginPrefs();
class BaseService {
//client实例
final _client = http.Client();
Map<String, String> headers = {
HttpHeaders.contentTypeHeader: "application/json",
HttpHeaders.authorizationHeader: loginPrefs.getToken(),
};
// String baseUrl = "http://192.168.1.189:8001";//
String baseUrl = "http://1.82.251.83:8001"; //线
String loginstatus = "登录失效";
//GET请求
getClient(String url, [bool useBaseUrl = true]) async {
try {
http.Response response = await _client
.get(Uri.parse(useBaseUrl ? baseUrl + url : url), headers: headers);
var res = json.decode(response.body);
if (res['message'].contains(loginstatus)) {
loginPrefs.clearLogin();
return;
}
return res;
} catch (e) {
print(e);
return {};
}
}
//POST请求
postClient(String url, body) async {
try {
var data = json.encode(body);
http.Response response = await _client.post(Uri.parse(baseUrl + url),
headers: headers, body: data);
var res = json.decode(response.body);
if (res['message'].contains(loginstatus)) {
loginPrefs.clearLogin();
return;
}
return res;
} catch (e) {
print(e);
return {};
}
}
}

View File

@ -0,0 +1,63 @@
class CoorTransModel {
int id;
// ignore: non_constant_identifier_names
double L0;
String belt;
String? calibs;
String dstEllipsoid;
double dx;
double dy;
double dz;
double wx;
double wy;
double wz;
double k;
double elevation;
int isMain;
String name;
double rota;
String srcEllipsoid;
CoorTransModel({
required this.name,
// ignore: non_constant_identifier_names
required this.L0,
required this.belt,
this.calibs,
required this.dstEllipsoid,
required this.dx,
required this.dy,
required this.dz,
required this.id,
required this.wx,
required this.wy,
required this.wz,
required this.k,
required this.elevation,
required this.isMain,
required this.rota,
required this.srcEllipsoid,
});
factory CoorTransModel.fromJson(Map<dynamic, dynamic> data) {
return CoorTransModel(
name: data['name'],
id: data['id'],
L0: data['L0'].toDouble(),
belt: data['belt'],
calibs: data['calibs'],
dstEllipsoid: data['dstEllipsoid'],
dx: data['dx'].toDouble(),
dy: data['dy'].toDouble(),
dz: data['dz'].toDouble(),
wx: data['wx'].toDouble(),
wy: data['wy'].toDouble(),
wz: data['wz'].toDouble(),
rota: data['rota'].toDouble(),
k: data['k'].toDouble(),
isMain: data['is_main'],
elevation: data['elevation'].toDouble(),
srcEllipsoid: data['srcEllipsoid'],
);
}
void forEach(Null Function(dynamic key, dynamic value) param0) {}
}

View File

@ -0,0 +1,244 @@
import 'package:get/get.dart';
import 'base_service.dart';
// String projCode = 'CJGKJEBYYB';
// String tid = "109";
// String projType = "hydraulic_tamping";
String projCode = 'TEST';
String tid = "1000";
String projType = "pile_cm";
BaseService service = BaseService();
class ProjController extends GetxController {
ProjController();
// 线
Future getSideLine() async {
Map res = await service.getClient(
'/api/comm/side_line/list?proj_code=$projCode&proj_type=$projType');
return res['data'];
}
// ---
Future getDeviceBind() async {
Map res = await service.getClient(
'/api/sys/device_bind/list?proj_type=$projType&proj_code=$projCode');
return res['data'];
}
//
getproject() async {
Map res = await service.getClient(
'/api/sys/project/list?org_code=a&proj_type=$projType&proj_code=$projCode');
return res['data'];
}
}
class LoginController extends GetxController {
//
getsmsCode(String phone) async {
Map res =
await service.postClient("/admin/base/open/smsCode", {"phone": phone});
return res;
}
//
phoneLogin(String phone, String smsCode) async {
Map res = await service.postClient(
"/admin/base/open/phone", {"phone": phone, "smsCode": smsCode});
return res;
}
//
getPerson() async {
Map res = await service.getClient("/admin/base/comm/person");
return res['data'];
}
//
getCaptcha() async {
Map res =
await service.getClient("/admin/base/open/captcha?height=40&width=150");
return res['data'];
}
getAccountLogin(String captchaId, String password, String username,
String verifyCode) async {
Map res = await service.postClient("/admin/base/open/login", {
"captchaId": captchaId,
"password": password,
"username": username,
"verifyCode": verifyCode
});
return res;
}
}
class PileCmController extends GetxController {
//
getRcordData(int page, int size, String date,
[String sort = "desc", String order = "pile_id"]) async {
Map res = await service.getClient(
"/api/$projType/record/page?page=$page&size=$size&org_code=a&proj_code=$projCode&tid=$tid&date=$date&sort=$sort&order=$order");
return res['data'];
}
getRcordList(String date, String? dateEnd) async {
dateEnd ??= date;
Map res = await service.getClient(
"/api/$projType/record/list?org_code=a&proj_code=$projCode&tid=$tid&date=$date&dateEnd=$dateEnd");
return res['data'];
}
//
getworkDateData() async {
Map res = await service.getClient(
"/api/$projType/record/work_date?org_code=a&proj_code=$projCode&tid=$tid");
if (res['code'] == 1000) {
return res['data'] ?? [];
} else {
return [];
}
}
//
getProcessData(int pileId) async {
Map res = await service.getClient(
"/api/$projType/process/list?pile_id=$pileId&proj_code=$projCode&tid=$tid");
return res['data'];
}
}
class GetServices {
BaseService service = BaseService();
// String projCode = 'CJGKJEBYYB';
// int tid = 109;
// String projType = "hydraulic_tamping";
String projCode = 'TEST';
int tid = 1000;
String projType = "pile_cm";
// 线
Future getSideLine() async {
try {
Map res = await service.getClient(
'/api/comm/side_line/list?proj_code=$projCode&proj_type=$projType');
return res['data'];
} catch (e) {
return {};
}
}
// ---
Future getDeviceBind() async {
Map res = await service.getClient(
'/api/sys/device_bind/list?proj_type=$projType&proj_code=$projCode');
return res['data'];
}
//
getproject() async {
Map res = await service
.getClient('/api/sys/project/list?org_code=a&proj_type=$projType');
return res['data'];
}
//
getRcordData(int page, int size, String date,
[String sort = "desc", String order = "tp_id"]) async {
Map res = await service.getClient(
"/api/$projType/record/page?page=$page&size=$size&org_code=a&proj_code=$projCode&tid=$tid&date=$date&sort=$sort&order=$order");
return res['data'];
}
//
// getRcordData(int page, int size, String date,
// [String sort = "desc", String order = "pile_id"]) async {
// Map res = await service.getClient(
// "/api/$projType/record/page?page=$page&size=$size&org_code=a&proj_code=$projCode&tid=$tid&date=$date&sort=$sort&order=$order");
// return res['data'];
// }
// getRcordList(String date, String? dateEnd) async {
// dateEnd ??= date;
// Map res = await service.getClient(
// "/api/$projType/record/list?org_code=a&proj_code=$projCode&tid=$tid&date=$date&dateEnd=$dateEnd");
// return res['data'];
// }
getRcordList(String date, [String? dateEnd]) async {
dateEnd ??= date;
Map res = await service.getClient(
"/api/$projType/record/list?org_code=a&proj_code=$projCode&tid=$tid&date=$date&dateEnd=$dateEnd");
if (res['code'] == 1000) {
return res['data'];
} else {
return [];
}
}
//
getworkDateData() async {
Map res = await service.getClient(
"/api/$projType/record/work_date?org_code=a&proj_code=$projCode&tid=$tid");
if (res['code'] == 1000) {
return res['data'] ?? [];
} else {
return [];
}
}
//
getProcessData(int pileId) async {
Map res = await service.getClient(
"/api/$projType/process/list?pile_id=$pileId&proj_code=$projCode&tid=$tid");
return res['data'];
}
//
getsmsCode(String phone) async {
Map res =
await service.postClient("/admin/base/open/smsCode", {"phone": phone});
return res;
}
//
phoneLogin(String phone, String smsCode) async {
Map res = await service.postClient(
"/admin/base/open/phone", {"phone": phone, "smsCode": smsCode});
return res;
}
//
getPerson() async {
Map res = await service.getClient("/admin/base/comm/person");
return res['data'];
}
//
getCaptcha() async {
Map res =
await service.getClient("/admin/base/open/captcha?height=40&width=150");
return res['data'];
}
getAccountLogin(String captchaId, String password, String username,
String verifyCode) async {
Map res = await service.postClient("/admin/base/open/login", {
"captchaId": captchaId,
"password": password,
"username": username,
"verifyCode": verifyCode
});
return res;
}
getRtuLast() async {
Map res = await service.getClient(
"/api/t2n/rtu/rtu_last?proj_type=$projType&proj_code=$projCode");
return res['data'];
}
getCoordTrans() async {
Map res = await service.getClient(
"/api/comm/coord_trans/list?proj_type=$projType&proj_code=$projCode");
return res['data'];
}
}

View File

@ -0,0 +1,75 @@
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'userModel.dart';
class LoginPrefs extends GetxController {
static const String expireStr = "expire"; //
static const String tokenStr = "token"; //token
static const String phoneStr = "phone";
final box = GetStorage(); // GetStorage
Future<String> init() async {
await GetStorage.init(); // GetStorage
return 'ok';
}
void saveExpire(int expire) {
box.write(expireStr, expire);
update();
}
int getExpire() {
return box.read(expireStr) ?? 0;
}
void saveToken(String token) {
box.write(tokenStr, token);
update();
}
String getToken() {
return box.read(tokenStr) ?? "";
}
void removeExpire() {
box.remove(expireStr);
update();
}
void removeToken() {
box.remove(tokenStr);
update();
}
void savePhone(String phone) {
box.write(phoneStr, phone);
update();
}
String getPhone() {
return box.read(phoneStr) ?? "";
}
void clearLogin() {
box.erase(); //
update();
}
}
class UserController extends GetxController {
final box = GetStorage(); // GetStorage
UserModel? getUser() {
final userMap = box.read('user');
if (userMap != null) {
return UserModel.fromJson(userMap);
}
return null;
}
void setUser(Map user) {
box.write('user', user);
update();
}
}

View File

@ -0,0 +1,53 @@
class UserModel {
int id;
String updateTime;
String departmentId;
String name;
int passwordV;
String nickName;
String headImg;
String phone;
String email;
int status;
String remark;
String orgCode;
String title;
String titleImg;
String username;
UserModel({
required this.name,
required this.departmentId,
required this.email,
required this.headImg,
required this.nickName,
required this.phone,
required this.title,
required this.titleImg,
required this.id,
required this.updateTime,
required this.orgCode,
required this.status,
required this.passwordV,
required this.remark,
required this.username,
});
factory UserModel.fromJson(Map<dynamic, dynamic> data) {
return UserModel(
name: data['name'],
departmentId: data['departmentId'],
email: data['email'] ?? "",
headImg: data['headImg'] ?? "",
nickName: data['nickName'] ?? "",
passwordV: data['passwordV'],
phone: data['phone'] ?? "",
title: data['title'] ?? "",
titleImg: data['title_img'] ?? "",
id: data['id'],
updateTime: data['updateTime'],
orgCode: data['org_code'],
status: data['status'],
remark: data['remark'] ?? "",
username: data['username'],
);
}
}

View File

@ -1,54 +1,445 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:flutter_app_update/flutter_app_update.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
// import 'controller/scence_map.dart';
import 'appbar/appbar.dart';
import 'login_in/connect/index.dart';
import 'login_in/connect/my_routes.dart';
import 'login_in/getx/index.dart';
import 'login_in/getx/real_data.dart';
import 'login_in/user/loginprefs.dart';
import 'screens/aimpoint_page.dart';
import 'package:scence_map/scence_map.dart';
import 'package:scence_map/controller.dart';
import 'screens/setting_page.dart';
void main() {
Get.lazyPut(() => ScenceMapController());
runApp(const MyApp());
Connect connect = Connect();
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); //
Get.put(RealController());
LoginPrefs loginPrefs = LoginPrefs();
String value =
await loginPrefs.init(); // await await等待异步方法执行完毕
if ('ok' == value) {
connect.init();
runApp(const EntryPage());
}
}
class MyApp extends StatelessWidget {
class EntryPage extends StatefulWidget {
const EntryPage({super.key});
@override
State<EntryPage> createState() => _EntryPageState();
}
class _EntryPageState extends State<EntryPage> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<bool>(
valueListenable: MyApp.isDarkMode,
builder: (context, isDarkMode, child) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const MyApp(),
initialRoute: "home",
onGenerateRoute: onGenerateRoute,
localizationsDelegates: GlobalMaterialLocalizations.delegates,
supportedLocales: const [
Locale('en', ''),
Locale('zh', ''),
Locale('he', ''),
Locale('es', ''),
Locale('ru', ''),
Locale('ko', ''),
Locale('hi', ''),
],
theme: isDarkMode ? ThemeData.dark() : ThemeData.light(),
// home: child,
);
},
child: const Text(""),
);
}
}
TextStyle textStyle = const TextStyle(fontSize: 15);
class MyApp extends StatefulWidget {
const MyApp({super.key});
// This widget is the root of your application.
static ValueNotifier<bool> isDarkMode = ValueNotifier<bool>(false);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
State<MyApp> createState() => _MyAppState();
}
//appbar
late bool isPortrait;
class _MyAppState extends State<MyApp> {
//
var _currentIndex = 1.obs;
late double appBarHeight;
var url = 'http://v5.rdc.pub/apk/';
///Flutter侧处理升级对话框
///[forcedUpgrade]
_showUpdateDialog(bool forcedUpgrade, String version) {
showDialog(
context: context,
barrierDismissible: !forcedUpgrade,
builder: (BuildContext context) {
return PopScope(
child: AlertDialog(
title: const Text('发现新版本'),
content: Text(
version,
style: const TextStyle(fontSize: 20),
),
actions: <Widget>[
if (!forcedUpgrade)
TextButton(
child: const Text('稍后升级'),
onPressed: () => Navigator.of(context).pop(),
),
TextButton(
child: const Text('升级'),
onPressed: () {
_appUpdate();
if (!forcedUpgrade) {
Navigator.of(context).pop();
}
},
),
],
),
);
},
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
_appUpdate() {
UpdateModel model = UpdateModel(
"${url}app-release.apk",
"app-release.apk",
"ic_launcher",
"",
);
AzhonAppUpdate.update(model).then((value) => debugPrint('//////$value'));
}
final String title;
bool comarableVersion(String version1, String version2) {
List<int> v1 = version1.split('.').map(int.parse).toList();
List<int> v2 = version2.split('.').map(int.parse).toList();
bool result = true;
for (int i = 0; i < 3; i++) {
if (v1[i] > v2[i]) {
result = true;
break;
} else if (v1[i] < v2[i]) {
result = false;
break;
}
}
return result;
}
@override
State<MyHomePage> createState() => _MyHomePageState();
}
void initState() {
GexRegister().registerDependencies();
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) async {
//
// PackageInfo packageInfo = await PackageInfo.fromPlatform();
// String version = packageInfo.version;
// String packageName = packageInfo.packageName;
// print("---$packageName,$version");
// var response =
// await http.Client().get(Uri.parse("${url}output-metadata.json"));
// Map json = jsonDecode(response.body);
class _MyHomePageState extends State<MyHomePage> {
// String onLineVersion = json["elements"][0]["versionName"] ?? "";
// // 线
// if (comarableVersion(version, onLineVersion) &&
// version != onLineVersion) {
// //
// _showUpdateDialog(false, onLineVersion);
// }
// AzhonAppUpdate.listener((map) {
// debugPrint(map['type']);
// });
});
}
final RealController controller1 = Get.find();
final List<Widget> _pages = [
ScenceMap(),
ScenceMap(),
ScenceMap(),
ScenceMap(),
SettingPortrait()
];
@override
Widget build(BuildContext context) {
return Scaffold(
// body: CounterPointer(),
body: ScenceMapView(
children: const [
AimPointer(),
],
onUpdate: (Offset offset, double scale, double rotation) {
print("onScale: scale: $scale, offset: $offset,rotation: $rotation");
},
),
);
final size = MediaQuery.of(context).size;
return OrientationBuilder(builder: ((context, orientation) {
isPortrait = Orientation.portrait == orientation ? true : false;
appBarHeight = Orientation.portrait == orientation ? 56.0 : 34.0;
return Scaffold(
resizeToAvoidBottomInset: false,
drawerEdgeDragWidth: 0.0, // drawer
endDrawer: _currentIndex.value == 1
? Drawer(
width: size.width * .8,
child: const AimPointer(),
)
: null,
appBar: PreferredSize(
preferredSize: Size.fromHeight(appBarHeight),
child: CustomAppBar(appBarHeight: 56, currentIndex: _currentIndex),
),
body: OrientationBuilder(
builder: (context, orientation) {
if (!isPortrait) {
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: _pages[_currentIndex.value],
),
const VerticalDivider(
width: 1, // 1
thickness: 1,
),
SizedBox(
width: 48,
child: SafeArea(
child: LayoutBuilder(builder: (context, constraints) {
//
// if (!isPortrait) {
// //
return Column(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Column(
children: [
IconButton(
onPressed: () {
setState(() {
_currentIndex.value = 0;
});
},
icon: const Icon(
Icons.date_range_rounded),
color: _currentIndex.value == 0
? const Color.fromARGB(
255, 60, 95, 123)
: Colors.grey,
),
Align(
alignment: Alignment.center,
child: Text(
'实时',
style: TextStyle(
fontSize: 12,
color: _currentIndex.value == 0
? const Color.fromARGB(
255, 60, 95, 123)
: Colors.grey,
),
),
),
],
),
const Divider(
height: 1, // 1
thickness: 1,
),
//
Column(
children: [
IconButton(
onPressed: () {
setState(() {
_currentIndex.value = 1;
});
},
icon: const Icon(Icons.map_outlined),
color: _currentIndex.value == 1
? const Color.fromARGB(
255, 60, 95, 123)
: Colors.grey,
),
Align(
alignment: Alignment.center,
child: Text(
'桩点',
style: TextStyle(
fontSize: 12,
color: _currentIndex.value == 1
? const Color.fromARGB(
255, 60, 95, 123)
: Colors.grey,
),
),
),
],
),
const Divider(),
Column(
children: [
IconButton(
onPressed: () {
setState(() {
_currentIndex.value = 2;
});
},
icon:
const Icon(Icons.my_location_sharp),
color: _currentIndex.value == 2
? const Color.fromARGB(
255, 60, 95, 123)
: Colors.grey,
),
Align(
alignment: Alignment.center,
child: Text(
'任务',
style: TextStyle(
fontSize: 12,
color: _currentIndex.value == 2
? const Color.fromARGB(
255, 60, 95, 123)
: Colors.grey,
),
),
),
],
),
const Divider(),
Column(
children: [
IconButton(
onPressed: () {
setState(() {
_currentIndex.value = 3;
});
},
icon: const Icon(
Icons.table_chart_outlined),
color: _currentIndex.value == 3
? const Color.fromARGB(
255, 60, 95, 123)
: Colors.grey,
),
Align(
alignment: Alignment.center,
child: Text(
'历史',
style: TextStyle(
fontSize: 12,
color: _currentIndex.value == 3
? const Color.fromARGB(
255, 60, 95, 123)
: Colors.grey,
),
),
),
],
),
const Divider(),
Column(
children: [
IconButton(
onPressed: () {
setState(() {
_currentIndex.value = 4;
});
},
icon: const Icon(Icons.settings),
color: _currentIndex.value == 4
? const Color.fromARGB(
255, 60, 95, 123)
: Colors.grey,
),
Align(
alignment: Alignment.center,
child: Text(
'设置',
style: TextStyle(
fontSize: 12,
color: _currentIndex.value == 4
? const Color.fromARGB(
255, 60, 95, 123)
: Colors.grey,
),
),
),
],
)
]);
//
}),
),
)
]);
} //
else {
//
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
flex: 12,
child: _pages[_currentIndex.value],
),
// VerticalDivider(),
Expanded(
flex: 1,
child: SizedBox(
height: 58,
child: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex.value,
onTap: (index) {
setState(() {
_currentIndex.value = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.date_range_rounded),
label: "实时"),
BottomNavigationBarItem(
icon: Icon(Icons.map_outlined),
label: "桩点"),
BottomNavigationBarItem(
icon: Icon(Icons.my_location_sharp),
label: "对点"),
BottomNavigationBarItem(
icon: Icon(Icons.table_chart_outlined),
label: "历史"),
BottomNavigationBarItem(
icon: Icon(Icons.settings), label: "设置"),
]),
),
)
]);
}
},
));
}));
}
}

View File

@ -5,7 +5,7 @@ import 'package:flutter/widgets.dart';
import 'package:scence_map/controller.dart';
import 'dart:ui' as ui;
import '../modules/device_type.dart';
import '../modules/common/device_type.dart';
final ScenceMapController mapcontroller = Get.put(ScenceMapController());
@ -17,7 +17,7 @@ class SightController extends GetxController {
var sightOffsetInit = const Offset(0, 0).obs;
var sightInit = const Offset(0, 0).obs;
// var lastSightOffset = const Offset(0, 0).obs; //
// var isCardVisible = false.obs; //
var isCardVisible = true.obs; //
var selectedRecord = Offset.zero.obs;
var initDx = 0.0.obs;
@ -35,8 +35,6 @@ class SightController extends GetxController {
}
}
final SightController sight = Get.put(SightController());
class SightView extends GetView<SightController> {
const SightView({super.key});
@ -48,7 +46,7 @@ class SightView extends GetView<SightController> {
final orientation = mediaQueryData.orientation; //
final deviceType = getDeviceType(context);
double rectWidth = size.height / 2;
if (!sight.isNomal.value) {
if (!controller.isNomal.value) {
rectWidth = size.width;
} else {
if (orientation == Orientation.landscape) {
@ -71,7 +69,7 @@ class SightView extends GetView<SightController> {
CustomPaint(
//
size: Size(rectWidth, rectWidth),
painter: DrawCicle(sight, 0, 0, isDarkMode),
painter: DrawCicle(controller, 0, 0, isDarkMode),
)
],
);
@ -195,11 +193,9 @@ class DrawCicle extends CustomPainter {
}
class DevicePointer extends StatelessWidget {
final DeviceItemController item;
final double maprotation;
final Offset targetPoint;
DevicePointer(this.item, this.maprotation, this.targetPoint);
// final DeviceItemController item;
// DevicePointer(this.item);
DevicePointer();
@override
Widget build(BuildContext context) {
@ -207,30 +203,19 @@ class DevicePointer extends StatelessWidget {
// double carHeight = item.height / controller.pixel2MeterRatio;
return Stack(
alignment: Alignment.center,
children: [
Obx(() {
// Offset offset = controller.xy2Screen(
// gnsscontroller.pilerCenter.X, gnsscontroller.pilerCenter.Y);
var dx = item.x - targetPoint.dx;
var dy = item.y - targetPoint.dy;
var left = dx * cos(maprotation) + dy * sin(maprotation);
var top = -dx * sin(maprotation) + dy * cos(maprotation);
SizedBox(
width: 60.0, //
height: 60.0, //
child: Image.asset(
"images/navi_pointer.png",
errorBuilder: (context, error, stackTrace) {
return Text('无法加载图片');
},
),
),
return Positioned(
left: left / mapcontroller.pixel2MeterRatio,
top: top / mapcontroller.pixel2MeterRatio,
child: Transform(
transform: Matrix4.identity()..rotateZ(item.rotation.value),
alignment: FractionalOffset.center,
child: Image.asset(
"images/pilerCar.png",
errorBuilder: (context, error, stackTrace) {
return Text('无法加载图片');
},
),
),
);
}),
// Obx(() {
// Offset offset = controller.xy2Screen(
// gnsscontroller.pilerCenter.X, gnsscontroller.pilerCenter.Y);

View File

@ -1,683 +0,0 @@
// ignore_for_file: non_constant_identifier_names
import 'dart:math';
class CoordGPS {
double LAT; //
double LNG; //
double? ALT; //
CoordGPS({this.ALT, required this.LAT, required this.LNG});
}
class CoordXYH {
double X;
double Y;
double H;
CoordXYH({required this.X, required this.Y, required this.H});
}
class CoordBLH {
double B;
double L;
double H;
CoordBLH({required this.B, required this.H, required this.L});
}
class CoordXYZ {
double X;
double Y;
double Z;
CoordXYZ({required this.X, required this.Y, required this.Z});
}
class PointXY {
double X;
double Y;
PointXY({required this.X, required this.Y});
}
class Region {
double X;
double Y;
double? width;
double? heigth;
double? D;
Region({this.D, required this.X, required this.Y, this.heigth, this.width});
}
class Ellipsoid {
late double A;
late double F;
late double B;
late double E12;
late double E22;
Ellipsoid(String name) {
const Map<String, Map<String, double>> datums = {
'BJ54': {'a': 6378245, 'f': 1 / 298.3},
'XA80': {'a': 6378140, 'f': 1 / 298.257},
'WGS84': {'a': 6378137, 'f': 1 / 298.257223563},
'WGS2000': {'a': 6378137, 'f': 1 / 298.257222101}
};
var Datum = datums[name];
if (Datum != null) {
double a = Datum['a']!;
double f = Datum['f']!;
double b = a - f * a;
double e12 = (a * a - b * b) / (a * a); //
double e22 = (a * a - b * b) / (b * b); //
A = a;
F = f;
B = b;
E12 = e12;
E22 = e22;
}
}
}
class TransParm {
late double dx;
late double dy;
late double dz;
late double rx;
late double ry;
late double rz;
late double rota;
late double k;
TransParm(TransOptions options) {
dx = options.dx ?? 0;
dy = options.dy ?? 0;
dz = options.dz ?? 0;
rx = options.rx ?? 0;
ry = options.ry ?? 0;
rz = options.rz ?? 0;
k = options.k ?? 0;
rota = options.rota ?? 0;
}
}
class TransOptions {
double? dx; //X平移(M)
double? dy; //Y平移(M)
double? dz; //z平移(M)
double? rx; //X旋转(°)
double? ry; //Y旋转(°)
double? rz; //Z旋转(°)
double? rota; //4
double? k; //(ppm)
double? L0; //线()
String? srcEllipsoid; //
String? dstEllipsoid; //
double? zoneWidth; //线
double? elevation; //
TransOptions({
this.L0,
this.dstEllipsoid,
this.dx,
this.dy,
this.dz,
this.elevation,
this.k,
this.rota,
this.rx,
this.ry,
this.rz,
this.srcEllipsoid,
});
}
//线
class CoordTrans {
late double L0; //线
late double zoneWidth; //
late double elevation; //
late double rota; //4
late Ellipsoid srcEllipsoid; //
late Ellipsoid dstEllipsoid; //
late TransParm transParm; //7
CoordTrans(TransOptions options) {
transParm = TransParm(options);
L0 = options.L0 ?? 0;
elevation = options.elevation ?? 0;
srcEllipsoid = Ellipsoid(options.srcEllipsoid ?? 'WGS84');
dstEllipsoid = Ellipsoid(options.dstEllipsoid ?? 'WGS2000');
zoneWidth = options.zoneWidth ?? 3;
}
CoordXYH GPS2XY(CoordGPS gps) {
return d2p(CoordBLH(H: gps.ALT ?? elevation, B: gps.LAT, L: gps.LNG));
}
//
CoordXYH d2p(CoordBLH blh) {
CoordXYZ zj = BLH2XYZ(blh);
CoordXYZ xzj = XYZ2XYZ(zj);
CoordBLH xdd = XYZ2BLH(xzj);
CoordXYH xpm = BL2XY(xdd);
return xpm;
}
//
CoordBLH p2d(CoordXYH xyh) {
CoordBLH bl = XY2BL(xyh, true);
CoordXYZ zj = BLH2XYZ(bl, true);
CoordXYZ xzj = XYZ2XYZ(zj, true);
CoordBLH xdd = XYZ2BLH(xzj, true);
return xdd;
}
setZoneWidth(width) {
if (width != 6 && width != 3) {
// print('带宽应当为6或者为3');
} else {
zoneWidth = width;
}
}
setL0(l0) {
L0 = l0;
}
int getZoneNo(double lng) {
// 6
//
if (zoneWidth == 6) {
return ((lng + 6) / 6).floor();
}
// 3
return ((lng + 1.5) / 3).floor();
}
PointXY xy2xyLocal(PointXY source) {
double x = source.X;
double y = source.Y;
double destx = x * transParm.k * cos(rota) -
y * transParm.k * sin(rota) +
transParm.dx;
double desty = x * transParm.k * sin(rota) +
y * transParm.k * cos(rota) +
transParm.dy;
return PointXY(X: destx, Y: desty);
}
CoordXYZ BLH2XYZ(CoordBLH pointBLH, [srcDst = false]) {
Ellipsoid ellipsoid = srcDst ? srcEllipsoid : dstEllipsoid;
double a = ellipsoid.A;
double e12 = ellipsoid.E12;
double radB = pointBLH.B / 180 * pi;
double radL = pointBLH.L / 180 * pi;
double H = pointBLH.H;
double N = a / sqrt(1 - e12 * sin(radB) * sin(radB)); //
double X = (N + H) * cos(radB) * cos(radL);
double Y = (N + H) * cos(radB) * sin(radL);
double Z = (N * (1 - e12) + H) * sin(radB);
return CoordXYZ(X: X, Y: Y, Z: Z);
}
/*
dX, dY, dZ:
wX, wY, wZ: ()
Kppm: ppm 1000000
*/
CoordXYZ XYZ2XYZ(CoordXYZ xyz, [srcDst = false]) {
double X = xyz.X;
double Y = xyz.Y;
double Z = xyz.Z;
double dX = transParm.dx;
double dY = transParm.dy;
double dZ = transParm.dz;
double rX = transParm.rx / 3600 / 180 * pi;
double rY = transParm.ry / 3600 / 180 * pi;
double rZ = transParm.rz / 3600 / 180 * pi;
double Kppm = transParm.k / 1000000;
CoordXYZ result = CoordXYZ(X: 0, Y: 0, Z: 0);
if (srcDst) {
result.X = X - dX - Kppm * X + rY * Z - rZ * Y;
result.Y = Y - dY - Kppm * Y - rX * Z + rZ * X;
result.Z = Z - dZ - Kppm * Z + rX * Y - rY * X;
} else {
result.X = X + dX + Kppm * X - rY * Z + rZ * Y;
result.Y = Y + dY + Kppm * Y + rX * Z - rZ * X;
result.Z = Z + dZ + Kppm * Z - rX * Y + rY * X;
}
return result;
}
/*
2
https://wenku.baidu.com/view/30a08f9ddd88d0d233d46a50.html
*/
CoordBLH XYZ2BLH(CoordXYZ XYZ, [srcDst = false]) {
double X = XYZ.X;
double Y = XYZ.Y;
double Z = XYZ.Z;
Ellipsoid ellipsoid = srcDst ? srcEllipsoid : dstEllipsoid;
double a = ellipsoid.A;
double b = ellipsoid.B;
double e12 = ellipsoid.E12;
double e22 = ellipsoid.E22;
double L = atan(Y / X);
//
double degL = L * 180 / pi;
// Y值为正 西
if (Y > 0) {
while (degL < 0) {
degL += 180;
}
while (degL > 180) {
degL -= 180;
}
} else {
while (degL > 0) {
degL -= 180;
}
while (degL < -180) {
degL += 180;
}
}
double tgU = Z / (sqrt(X * X + Y * Y) * sqrt(1 - e12));
double U = atan(tgU);
double tgB = (Z + b * e22 * pow(sin(U), 3)) /
(sqrt(X * X + Y * Y) - a * e12 * pow(cos(U), 3));
double B = atan(tgB);
double degB = B * 180 / pi; //
if (Z > 0) {
// Z值为正
while (degB < 0) {
degB += 90;
}
while (degB > 90) {
degB -= 90;
}
} else {
while (degB > 0) {
degB -= 90;
}
while (degB < -90) {
degB += 90;
}
}
while (degB < 0) {
degB += 360;
}
while (degB > 360) {
degB -= 360;
}
double N = a / sqrt(1 - e12 * sin(B) * sin(B)); //
double H = 0;
// B接近极区 ±90°
if ((degB).abs() > 80) {
H = Z / sin(B) - N * (1 - e12);
} else {
H = sqrt(X * X + Y * Y) / cos(B) - N;
}
return CoordBLH(B: degB, L: degL, H: H);
}
/*
prjHeight:
http://www.cnblogs.com/imeiba/p/5696967.html
*/
CoordXYH BL2XY(CoordBLH BLH,
[srcDst = false, offsetY = 500000, offsetX = 0]) {
Ellipsoid ellipsoid = srcDst ? srcEllipsoid : dstEllipsoid;
double a = ellipsoid.A;
double b = ellipsoid.B;
double e12 = ellipsoid.E12;
double e22 = ellipsoid.E22;
if (L0 == 0) {
int zoneNo = getZoneNo(BLH.L);
L0 = (zoneNo - 0.5) * zoneWidth;
}
double radL0 = L0 / 180 * pi;
double radB = BLH.B / 180 * pi;
double radL = BLH.L / 180 * pi;
double N = a / sqrt(1 - e12 * sin(radB) * sin(radB)); //
double T = tan(radB) * tan(radB);
double C = e22 * cos(radB) * cos(radB);
double A = (radL - radL0) * cos(radB);
double M = a *
((1 - e12 / 4 - 3 * e12 * e12 / 64 - 5 * e12 * e12 * e12 / 256) * radB -
(3 * e12 / 8 + 3 * e12 * e12 / 32 + 45 * e12 * e12 * e12 / 1024) *
sin(2 * radB) +
(15 * e12 * e12 / 256 + 45 * e12 * e12 * e12 / 1024) *
sin(4 * radB) -
(35 * e12 * e12 * e12 / 3072) * sin(6 * radB));
//x,y的计算公式见孔祥元等主编武汉大学出版社2002年出版的72
//( [
double x = M +
N *
tan(radB) *
(A * A / 2 +
(5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 +
(61 - 58 * T + T * T + 600 * C - 330 * e22) *
A *
A *
A *
A *
A *
A /
720);
double y = N *
(A +
(1 - T + C) * A * A * A / 6 +
(5 - 18 * T * T * T + 72 * C - 58 * e22) * A * A * A * A * A / 120);
x = offsetX + x * (b + elevation) / b;
y = offsetY + y * (b + elevation) / b;
return CoordXYH(
X: (x * 1000).round() / 1000, Y: (y * 1000).round() / 1000, H: BLH.H);
}
/*
prjHeight:
http://www.cnblogs.com/imeiba/p/5696967.html
*/
CoordBLH XY2BL(CoordXYH xyh,
[srcDst = false, offsetY = 500000, offsetX = 0]) {
Ellipsoid ellipsoid = srcDst ? srcEllipsoid : dstEllipsoid;
double a = ellipsoid.A;
double b = ellipsoid.B;
double e12 = ellipsoid.E12;
double e22 = ellipsoid.E22;
double e1 = (1 - sqrt(1 - e12)) / (1 + sqrt(1 - e12));
double radL0 = L0 / 180 * pi;
//
double Y = xyh.Y % 1000000;
double x = (xyh.X - offsetX) * b / (b + elevation);
double y = (Y - offsetY) * b / (b + elevation);
double u = x /
(a * (1 - e12 / 4 - 3 * e12 * e12 / 64 - 5 * e12 * e12 * e12 / 256));
double fai = u +
(3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * sin(2 * u) +
(21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * sin(4 * u) +
(151 * e1 * e1 * e1 / 96) * sin(6 * u) +
(1097 * e1 * e1 * e1 * e1 / 512) * sin(8 * u);
double C = e22 * cos(fai) * cos(fai);
double T = tan(fai) * tan(fai);
double N = a / sqrt(1 - e12 * sin(fai) * sin(fai));
double R = a *
(1 - e12) /
sqrt((1 - e12 * sin(fai) * sin(fai)) *
(1 - e12 * sin(fai) * sin(fai)) *
(1 - e12 * sin(fai) * sin(fai)));
double D = y / N;
double L = radL0 +
(D -
(1 + 2 * T + C) * D * D * D / 6 +
(5 - 2 * C + 28 * T - 3 * C * C + 8 * e22 + 24 * T * T) *
D *
D *
D *
D *
D /
120) /
cos(fai);
double B = fai -
(N * tan(fai) / R) *
(D * D / 2 -
(5 + 3 * T + 10 * C - 4 * C * C - 9 * e22) *
D *
D *
D *
D /
24 +
(61 + 90 * T + 298 * C + 45 * T * T - 256 * e22 - 3 * C * C) *
D *
D *
D *
D *
D *
D /
720);
B = B * 180 / pi;
L = L * 180 / pi;
return CoordBLH(B: B, L: L, H: xyh.H);
}
//
dist(PointXY p1, PointXY p2) {
double d = sqrt(pow((p2.X - p1.X), 2) + pow((p2.Y - p1.Y), 2));
return d;
}
//
double fwj(PointXY p1, PointXY p2) {
double dx = p2.X - p1.X;
double dy = p2.Y - p1.Y;
return pi - sign(dy) - atan(dx / dy);
}
//
TransOptions calcThree(CoordBLH p1, CoordXYH p2) {
CoordXYZ source = BLH2XYZ(p1);
CoordBLH bl = XY2BL(p2, true);
CoordXYZ dest = BLH2XYZ(bl, true);
double dX = dest.X - source.X;
double dY = dest.Y - source.Y;
double dZ = dest.Z - source.Z;
return TransOptions(dx: dX, dy: dY, dz: dZ);
}
// TransOptions calcFour(List p1, List p2, int PointCount) {
// double rota = 0;
// double scale = 0;
// double dx = 0;
// double dy = 0;
// if (PointCount == 2) {
// rota = fwj(p2[0] as PointXY, p2[1]) - fwj(p1[0], p1[1]);
// scale = dist(p2[0], p2[1]) / dist(p1[0], p1[1]);
// dx = p2[0].X - scale * cos(rota) * p1[0].X + scale * sin(rota) * p1[0].Y;
// dy = p2[0].Y - scale * sin(rota) * p1[0].X - scale * cos(rota) * p1[0].Y;
// } else if (PointCount > 2) {
// double u = 1.0, v = 0, Dx = 0.0, Dy = 0.0;
// int intCount = PointCount;
// Matrix4 dx1 = Matrix.zeros(4, 1);
// // Matrix4 B1 = Matrix.zeros(2 * intCount, 4);
// Matrix4 B1 = Matrix4.zero();
// for (int i = 0; i < 2 * intCount; i++) {
// for (int j = 0; j < 4; j++) {
// B1.setEntry(i, j, 0);
// }
// }
// Matrix4 W1 = Matrix.zeros(2 * intCount, 1);
// // Matrix4 BT = Matrix.zeros(4, 2 * intCount);
// Matrix4 BT = Matrix4.zero();
// for (int i = 0; i < 4; i++) {
// for (int j = 0; j < 2 * intCount; j++) {
// BT.setEntry(i, j, 0);
// }
// }
// Matrix4 N = Matrix4(
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// );
// Matrix4 InvN = Matrix4(
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// );
// Matrix4 BTW = Matrix.zeros(4, 1);
// for (int i = 0; i < intCount; i++) {
// //
// B1.setEntry(2 * i, 0, 1);
// B1.setEntry(2 * i, 1, 0);
// B1.setEntry(2 * i, 2, p1[i].X);
// B1.setEntry(2 * i, 3, -p1[i].Y);
// B1.setEntry(2 * i + 1, 0, 0);
// B1.setEntry(2 * i + 1, 1, 1);
// B1.setEntry(2 * i + 1, 2, p1[i].Y);
// B1.setEntry(2 * i + 1, 3, p1[i].X);
// // B1[2 * i][0] = 1;
// // B1[2 * i][1] = 0;
// // B1[2 * i][2] = p1[i].X;
// // B1[2 * i][3] = -p1[i].Y;
// // B1[2 * i + 1][0] = 0;
// // B1[2 * i + 1][1] = 1;
// // B1[2 * i + 1][2] = p1[i].Y;
// // B1[2 * i + 1][3] = p1[i].X;
// }
// for (int i = 0; i < intCount; i++) {
// //
// // W1[2 * i][0] = p2[i].X - u * p1[i].X + v * p1[i].Y - Dx;
// // W1[2 * i + 1][0] = p2[i].Y - u * p1[i].Y - v * p1[i].X - Dy;
// W1.setEntry(2 * i, 0, p2[i].X - u * p1[i].X + v * p1[i].Y - Dx);
// W1.setEntry(2 * i + 1, 0, p2[i].Y - u * p1[i].Y - v * p1[i].X - Dy);
// }
// //
// Matrix4 b1 = Matrix4.identity();
// // B1
// BT = Matrix4.copy(b1);
// BT.transpose();
// //
// N = BT * B1;
// //
// InvN = N.clone();
// InvN.invert();
// BTW = BT * (W1);
// dx1 = InvN * (BTW);
// Dx = Dx + dx1[0][0];
// Dy = Dy + dx1[1][0];
// u = u + dx1[2][0];
// v = v + dx1[3][0];
// dx = Dx;
// dy = Dy;
// rota = atan(v / u);
// scale = u / cos(rota);
// }
// return TransOptions(rota: rota, k: scale, dx: dx, dy: dy);
// }
// //
// TransOptions calcBuras(List p1, List p2, pointCount) {
// let B1 = Matrix.zeros(pointCount * 3, 7);
// let dx1 = Matrix.zeros(7, 1);
// let L = Matrix.zeros(pointCount * 3, 1);
// let BT = Matrix.zeros(7, pointCount * 3);
// let N = Matrix.zeros(7, 7);
// let InvN = Matrix.zeros(7, 7);
// let BTL = Matrix.zeros(7, 1);
// for (int i = 0; i < pointCount * 3; i++) {
// if (i % 3 == 0) {
// L[i][0] = p2[(i / 3).floor()].X;
// } else if (i % 3 == 1) {
// L[i][0] = p2[(i / 3).floor()].Y;
// } else if (i % 3 == 2) {
// L[i][0] = p2[(i / 3).floor()].Z;
// }
// }
// for (int i = 0; i < pointCount * 3; i++) {
// if (i % 3 == 0) {
// int index = (i / 3) as int;
// B1[i][0] = 1;
// B1[i][1] = 0;
// B1[i][2] = 0;
// B1[i][3] = p1[index].X;
// B1[i][4] = 0;
// B1[i][5] = -p1[index].Z;
// B1[i][6] = p1[index].Y;
// } else if (i % 3 == 1) {
// int index = ((i - 1) / 3) as int;
// B1[i][0] = 0;
// B1[i][1] = 1;
// B1[i][2] = 0;
// B1[i][3] = p1[index].Y;
// B1[i][4] = p1[index].Z;
// B1[i][5] = 0;
// B1[i][6] = -p1[index].X;
// } else if (i % 3 == 2) {
// int index = ((i - 2) / 3) as int;
// B1[i][0] = 0;
// B1[i][1] = 0;
// B1[i][2] = 1;
// B1[i][3] = p1[index].Z;
// B1[i][4] = -p1[index].Y;
// B1[i][5] = p1[index].X;
// B1[i][6] = 0;
// }
// }
// Matrix4 b1 = Matrix4.identity();
// BT = B1.transpose();
// N = BT.mmul(B1);
// InvN = inverse(N);
// BTL = BT.mmul(L);
// dx1 = InvN.mmul(BTL);
// double dx = dx1[0][0];
// double dy = dx1[1][0];
// double dz = dx1[2][0];
// double scale = dx1[3][0];
// double rotax = dx1[4][0] / dx1[3][0];
// double rotay = dx1[5][0] / dx1[3][0];
// double rotaz = dx1[6][0] / dx1[3][0];
// return TransOptions(
// dx: dx, dy: dy, dz: dz, k: scale, rx: rotax, ry: rotay, rz: rotaz);
// }
double sign(num number) {
if (number < 0) {
return -1.0;
} else if (number > 0) {
return 1.0;
} else {
return 0.0;
}
}
}

View File

@ -1,13 +1,15 @@
import 'dart:async';
import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:scence_map/controller.dart';
import 'package:scence_map/scence_map.dart';
import '../modules/device_type.dart';
import '../map_pointer/modules/common/device_type.dart';
import '../draws/aim_pointer.dart';
import '../map_pointer/draws/aim_pointer.dart';
final SightController sight = Get.put(SightController());
@ -23,6 +25,10 @@ class AimPointer extends StatefulWidget {
// CounterPointer
class _AimPointerState extends State<AimPointer> {
// late final DeviceItemController item;
// late final double maprotation;
// late final Offset targetPoint;
@override
void initState() {
super.initState();
@ -41,6 +47,11 @@ class _AimPointerState extends State<AimPointer> {
bool isPortrait = Orientation.portrait == orientation ? true : false;
double rectWidth = size.width;
final deviceType = getDeviceType(context);
// var dx = item.x - targetPoint.dx;
// var dy = item.y - targetPoint.dy;
// var left = dx * cos(maprotation) + dy * sin(maprotation);
// var top = -dx * sin(maprotation) + dy * cos(maprotation);
if (orientation == Orientation.landscape) {
rectWidth = size.width / 2 - 60;
if (deviceType == DeviceType.mobile) {
@ -58,6 +69,10 @@ class _AimPointerState extends State<AimPointer> {
width: rectWidth,
height: rectWidth,
child: Card(
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.transparent, width: 0),
borderRadius: BorderRadius.circular(4.0), //
),
child: Stack(
children: [
Container(
@ -92,7 +107,7 @@ class _AimPointerState extends State<AimPointer> {
);
})),
Positioned(
top: 10,
top: 25,
right: 10,
child: TextButton(
child: Text(
@ -101,6 +116,33 @@ class _AimPointerState extends State<AimPointer> {
),
onPressed: () {},
)),
Positioned(
right: 0,
top: 0,
child: IconButton(
icon: const Icon(Icons.close),
onPressed: () {
//
sight.isCardVisible.value = false;
},
),
),
Positioned(
// left: rectWidth / 2 + left / mapcontroller.pixel2MeterRatio,
// top: rectWidth / 2 - top / mapcontroller.pixel2MeterRatio,
left: 70,
top: 70,
// child: Obx(() {
// return Transform(
// // transform: Matrix4.identity()..rotateZ(item.rotation.value),
// transform: Matrix4.identity()..rotateZ(0),
// alignment: FractionalOffset.center,
// child: DevicePointer(item),
// );
// }),
child: DevicePointer(),
)
],
),
),
@ -125,75 +167,79 @@ class _AimPointerState extends State<AimPointer> {
}
}
// List<Widget> createDevice(ScenceMapController controller) {
// //
// if (controller.isDrag) {
// return [];
// }
// List<Widget> devices = [];
// controller.deviceList.forEach((key, item) {
// devices.add(Obx(() {
// Offset offset = controller.xy2Screen(
// gnsscontroller.pilerCenter.X, gnsscontroller.pilerCenter.Y);
class ScenceMap extends StatelessWidget {
const ScenceMap();
@override
Widget build(BuildContext context) {
//
final Size screenSize = MediaQuery.of(context).size;
// double carWidth = item.width / controller.pixel2MeterRatio;
// double carHeight = item.height / controller.pixel2MeterRatio;
// print("${offset.dx - carHeight * 2},${offset.dy - carWidth}");
// return Stack(
// children: [
// Obx(() {
// // item.update.value;
// gnsscontroller.locationUpdate.value;
return Container(
width: screenSize.width,
height: screenSize.height,
child: ScenceMapView(
children: const [
SightGview(),
],
onUpdate: (Offset offset, double scale, double rotation) {
print("onScale: scale: $scale, offset: $offset,rotation: $rotation");
},
),
);
}
}
// Offset offset = controller.xy2Screen(
// gnsscontroller.pilerCenter.X, gnsscontroller.pilerCenter.Y);
// return Positioned(
// left: offset.dx - carHeight * 3.5,
// top: offset.dy - carWidth / 2,
// // width: ,
// height: carWidth,
// child: Transform(
// transform: Matrix4.identity()
// // ..scale(item.scale)
//
class SightGview extends StatelessWidget {
const SightGview();
// ..rotateZ(gnsscontroller.pilerCenter.rotation),
// alignment: FractionalOffset.center,
// // child: Image.network(
// // "http://v5.rdc.pub${item.image[item.status.value]}",
// // errorBuilder: (context, error, stackTrace) {
// // return Image.asset("")
// // child: Image.asset("images/pilerCar.png",
// // errorBuilder: (context, error, stackTrace) {
// // return Text('无法加载图片');
// // }
// // // width: item.width,
// // // height: item.height,
// // )
// child: Image.asset(
// "images/pilerCar.png",
// errorBuilder: (context, error, stackTrace) {
// return Text('无法加载图片');
// },
// // width: item.width,
// // height: item.height,
// ),
// ));
// }),
// Positioned(
// left: offset.dx,
// top: offset.dy,
// child: Transform(
// transform: Matrix4.identity()
// ..rotateZ(-controller.rotation.value),
// child: Column(
// children: [Text("设备:${item.name}")],
// ),
// ))
// ],
// );
// }));
// });
// return devices;
// }
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
MediaQueryData mediaQueryData =
MediaQueryData.fromView(WidgetsBinding.instance.window); //
final orientation = mediaQueryData.orientation; //
bool isPortrait = Orientation.portrait == orientation ? true : false;
double rectWidth = size.width;
final deviceType = getDeviceType(context);
if (orientation == Orientation.landscape) {
rectWidth = size.width / 2 - 60;
if (deviceType == DeviceType.mobile) {
rectWidth = size.height - 130;
}
} else {
rectWidth = size.height / 2;
if (deviceType == DeviceType.mobile) {
rectWidth = size.width - 5;
}
}
return Obx(() => Positioned(
left: sight.sightOffset.value.dx,
top: sight.sightOffset.value.dy,
width: rectWidth + 15,
height: rectWidth + 15,
child: GestureDetector(
onScaleStart: (details) {
//
sight.sightInit.value =
details.localFocalPoint - sight.sightOffset.value;
},
onScaleUpdate: (details) {
// 使
sight.sightOffset.value =
details.localFocalPoint - sight.sightInit.value;
},
child: Visibility(
visible: sight.isCardVisible.value, //
child: Container(
decoration: const BoxDecoration(color: Colors.transparent),
child: const Stack(children: [
Card(
color: Colors.transparent,
elevation: 5.0,
child: AimPointer(),
),
]),
)))));
}
}

392
lib/screens/login_page.dart Normal file
View File

@ -0,0 +1,392 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../login_in/user/loginprefs.dart';
import '../login_in/service/service.dart';
class Login extends StatefulWidget {
const Login({super.key});
@override
State<Login> createState() => _LoginState();
}
class _LoginState extends State<Login> with SingleTickerProviderStateMixin {
//with SingleTickerProviderStateMixin 使this
late TabController _tabController;
//
@override
void initState() {
_tabController = TabController(length: 2, vsync: this);
super.initState();
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('欢迎登录'),
),
body: Column(
children: <Widget>[
TabBar(
labelColor: Colors.black, //
unselectedLabelColor: Colors.grey, //
labelStyle: const TextStyle(color: Colors.black), //
unselectedLabelStyle:
const TextStyle(color: Colors.grey), //
controller: _tabController,
tabs: const [
Tab(text: '手机号登录', icon: Icon(Icons.phone_android)),
Tab(text: '账号登录', icon: Icon(Icons.account_box_outlined)),
],
),
Expanded(
child: TabBarView(
controller: _tabController,
children: const [
PhoneLoginPage(),
AccountLoginPage(),
],
),
),
],
),
);
}
}
class AccountLoginPage extends StatefulWidget {
const AccountLoginPage({super.key});
@override
State<AccountLoginPage> createState() => _AccountLoginPageState();
}
class _AccountLoginPageState extends State<AccountLoginPage> {
//FormState
final _formKey = GlobalKey<FormState>();
String username = "";
String password = "";
String verifyCode = "";
String svgString = "";
String captchaId = "";
@override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) async {
await changeCaptcha();
});
}
changeCaptcha() async {
var captcha = await GetServices().getCaptcha();
setState(() {
svgString = captcha['data'];
captchaId = captcha['captchaId'];
});
}
showErrorSnackbar(BuildContext context, String text) {
final snackBar = SnackBar(
content: Text(text),
backgroundColor: Colors.red,
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
LoginPrefs loginPrefs = LoginPrefs();
bool _obscureText = false;
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Form(
key: _formKey,
child: Column(
children: [
//
TextFormField(
decoration: const InputDecoration(
labelText: '用户名:',
),
onSaved: (String? value) => username = value!,
validator: (String? value) {
String str = "";
RegExp regex = RegExp(r'^[a-zA-Z0-9]+$');
if (value!.isEmpty) {
str = '用户名不能为空';
} else if (!regex.hasMatch(value)) {
str = '请输入有效的账号';
}
return str == "" ? null : str;
},
),
const SizedBox(height: 16.0),
//
TextFormField(
decoration: InputDecoration(
labelText: '密码:',
suffixIcon: IconButton(
icon: Icon(
_obscureText
? Icons.visibility
: Icons.visibility_off,
),
onPressed: () {
setState(() {
_obscureText = !_obscureText; //
});
}),
),
obscureText: _obscureText,
onSaved: (String? value) => password = value!,
validator: (String? value) {
String str = "";
RegExp regex = RegExp(r'^[a-zA-Z0-9@#$]+$');
if (value!.isEmpty) {
str = '密码不能为空';
} else if (!regex.hasMatch(value)) {
str = '请输入有效的密码';
}
return str == "" ? null : str;
},
),
const SizedBox(height: 24.0),
Row(
children: [
Expanded(
child: TextFormField(
decoration: const InputDecoration(
labelText: '验证码:',
),
keyboardType: TextInputType.phone,
validator: (String? value) {
String str = "";
if (value!.isEmpty) {
str = '验证码不能为空';
}
return str == "" ? null : str;
},
onSaved: (String? value) => verifyCode = value!,
),
),
ElevatedButton(
onPressed: () async {
changeCaptcha();
},
child: svgString.isNotEmpty
? SvgPicture.string(
svgString,
width: 70, //
height: 40, //
color: Colors.black,
)
: const Text(""))
],
),
//
ElevatedButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save(); //
var res = await GetServices().getAccountLogin(
captchaId, password, username, verifyCode);
setState(() {
if (res['code'] != 1000) {
changeCaptcha();
showErrorSnackbar(context, res["message"]);
} else {
loginPrefs.saveExpire((res["data"]["expire"]));
loginPrefs.saveToken((res["data"]["token"]));
Navigator.pushNamed(context, 'home'); //
}
});
}
},
child: const Text('登录'),
),
],
))
],
),
),
);
}
}
class PhoneLoginPage extends StatefulWidget {
const PhoneLoginPage({super.key});
@override
State<PhoneLoginPage> createState() => _PhoneLoginPageState();
}
class _PhoneLoginPageState extends State<PhoneLoginPage> {
String phoneNumber = "";
String smsCode = "";
bool issmsCode = false;
String smsText = "获取验证码";
int _seconds = 60;
late Timer _timer;
//FormState
final _formKey = GlobalKey<FormState>();
LoginPrefs loginPrefs = LoginPrefs();
@override
void initState() {
super.initState();
phoneNumber = loginPrefs.getPhone();
}
void startTimer() {
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (_seconds > 0) {
_seconds -= 1;
smsText = "重新获取(${_seconds}s)";
} else {
_timer.cancel(); // 0
smsText = "获取验证码";
}
});
}
showErrorSnackbar(BuildContext context, String text) {
final snackBar = SnackBar(
content: Text(text),
backgroundColor: Colors.red,
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
@override
Widget build(BuildContext context) {
return Container(
key: _scaffoldKey,
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Form(
key: _formKey,
child:
//
Column(
children: [
TextFormField(
maxLength: 11,
initialValue: phoneNumber,
decoration: const InputDecoration(
labelText: '手机号',
),
keyboardType: TextInputType.phone,
validator: (String? value) {
String str = "";
final RegExp phoneRegex = RegExp(r'^[1-9]\d{10}$');
if (value!.isEmpty) {
str = '手机号不能为空';
} else if (!phoneRegex.hasMatch(value)) {
str = '请输入有效的手机号';
}
return str == "" ? null : str;
},
onSaved: (String? value) => phoneNumber = value!,
),
Row(
children: [
Expanded(
child: TextFormField(
maxLength: 6,
decoration: const InputDecoration(
labelText: '验证码',
),
keyboardType: TextInputType.phone,
validator: (String? value) {
String str = "";
if (value!.isEmpty) {
str = '验证码不能为空';
}
if (issmsCode) {
return null;
}
return str == "" ? null : str;
},
onSaved: (val) => smsCode = val!,
),
),
//
ElevatedButton(
onPressed: () async {
issmsCode = true;
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
//
FocusScope.of(context).unfocus();
await GetServices().getsmsCode(phoneNumber);
startTimer(); //
}
},
child: Text(smsText),
),
],
),
const SizedBox(height: 16.0),
//
ElevatedButton(
onPressed: () async {
issmsCode = false;
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
// ...
//
FocusScope.of(context).unfocus();
var res = await GetServices()
.phoneLogin(phoneNumber, smsCode);
setState(() {
if (res['code'] != 1000) {
showErrorSnackbar(
_scaffoldKey.currentContext!, res["message"]);
} else {
loginPrefs.savePhone(phoneNumber);
loginPrefs.saveExpire(res["data"]["expire"]);
loginPrefs.saveToken(res["data"]["token"]);
Navigator.pushNamed(
_scaffoldKey.currentContext!, 'home'); //
}
});
}
},
child: const Text('登录'),
),
],
)),
],
),
),
);
}
}

View File

@ -0,0 +1,162 @@
//
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import '../login_in/connect/bluetooth_page.dart';
import '../login_in/connect/config.dart';
import '../login_in/connect/connect_type.dart';
import '../login_in/getx/blue_tooth.dart';
import '../setting/antenna_setting.dart';
import '../setting/person_details.dart';
import '../setting/wifi_page.dart';
import '../setting/xy_change.dart';
class ListTileButton extends StatelessWidget {
final ListItem item;
const ListTileButton({super.key, required this.item, isInput = false});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => item.onPressed(),
child: ListTile(
leading: Icon(item.icon),
title: Text(item.text),
),
);
}
}
class ListItem {
String text;
final IconData icon;
final Function onPressed;
ListItem(this.text, this.icon, this.onPressed);
}
TextStyle textStyle = const TextStyle(fontSize: 15);
// //
// PermissionStatus? _status;
final BlueToothController blueToothController = Get.put(BlueToothController());
String connectedID = "";
//
class SettingPortrait extends StatefulWidget {
const SettingPortrait({super.key});
@override
State<SettingPortrait> createState() => _SettingPortraitState();
}
class _SettingPortraitState extends State<SettingPortrait> {
String userName = "";
List<ListItem> items = [];
void readID() {
setState(() {
connectedID = blueToothController.connectedDeviceID.value;
});
}
@override
void initState() {
super.initState();
items = [
ListItem('连接设置', Icons.change_circle_outlined, () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("选择连接方式"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: const Text("蓝牙连接"),
onTap: () {
// items[0].text = "连接设置(蓝牙)";
AppConfig.updateConfig("connectType", "bluetooth");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const BlueTooth()),
);
},
),
ListTile(
title: const Text("WiFi连接"),
onTap: () {
blueToothController.connectedType.value =
ConnectType.wifi;
AppConfig.updateConfig("connectType", "wifi");
// items[0].text = "连接设置(WiFi)";
// Connectivity()
// .checkConnectivity()
// .then((connectivityResult) {
// if (connectivityResult != ConnectivityResult.wifi) {
// OpenSettings.openWIFISetting();
// } else {
// }
// });
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const WifiPage()),
);
},
),
],
),
);
},
);
}),
// ListItem('规划点', Icons.download_outlined, () {
// Navigator.push(context,
// MaterialPageRoute(builder: (context) => const PlanPoint()));
// }),
ListItem('坐标转换', Icons.settings, () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const XyChangeView()));
}),
ListItem('个人中心', Icons.person_outline_outlined, () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const PersonDetails()));
}),
ListItem('天线位置设置', Icons.settings_input_antenna, () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const AntennaSetting()));
}),
];
SchedulerBinding.instance.addPostFrameCallback((_) async {});
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return Column(
children: <Widget>[
Obx(() {
blueToothController.connectedType.value;
return SizedBox(
height: 40,
child: ListTileButton(
item: items[index],
));
}),
const Divider(), // 线
],
);
},
);
}
}

View File

@ -0,0 +1,290 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../login_in/connect/blue_tooth.dart';
import '../login_in/connect/index.dart';
import '../login_in/getx/blue_tooth.dart';
final BlueToothController blueToothController = Get.put(BlueToothController());
BlueSetting blueSetting = BlueSetting();
class AntennaSetting extends GetView<AntennaController> {
const AntennaSetting({super.key});
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final isPortrait =
MediaQuery.of(context).orientation == Orientation.portrait;
// brightness信息
Brightness currentBrightness = Theme.of(context).brightness;
bool isDarkMode = currentBrightness == Brightness.dark;
Widget simpleDevice = Card(
clipBehavior: Clip.hardEdge,
child: CustomPaint(
size: isPortrait
? Size(size.width, size.height / 2)
: Size(size.width / 2, size.height - 80),
painter: SimpleDevice(controller, isDarkMode),
),
);
List<Widget> inputList = [
// const Text("中心点位置(m):"),
// Row(
// children: [
// Expanded(
// child: TextFormField(
// enabled: false, // false以禁止输入
// initialValue:
// controller.centerPoint.value.dx.toStringAsFixed(3),
// keyboardType: TextInputType.number,
// decoration: const InputDecoration(prefixText: 'x:'),
// validator: (value) {
// if (value == null || value.isEmpty) {
// return '请输入';
// }
// return null;
// },
// onChanged: (String value) {
// if (value != "") {
// double point = double.parse(value);
// if (point != 0) {
// controller.centerPoint.value =
// Offset(point, controller.centerPoint.value.dy);
// }
// }
// }),
// ),
// const SizedBox(
// width: 5,
// ),
// Expanded(
// child: TextFormField(
// enabled: false, // false以禁止输入
// initialValue:
// controller.centerPoint.value.dy.toStringAsFixed(3),
// keyboardType: TextInputType.number,
// decoration: const InputDecoration(prefixText: 'y:'),
// validator: (value) {
// if (value == null || value.isEmpty) {
// return '请输入';
// }
// return null;
// },
// onChanged: (String value) {
// if (value != "") {
// double point = double.parse(value);
// if (point != 0) {
// controller.centerPoint.value =
// Offset(controller.centerPoint.value.dx, point);
// }
// }
// }),
// )
// ],
// ),
const Text("左侧位置(m):"),
Row(
children: [
Expanded(
child: TextFormField(
initialValue: controller.leftPoint.value.dx.toStringAsFixed(3),
keyboardType: TextInputType.number,
decoration: const InputDecoration(prefixText: 'x:'),
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入值';
}
return null;
},
onChanged: (String value) {
if (value != "") {
double point = double.parse(value);
if (point != 0) {
controller.leftPoint.value =
Offset(point, controller.leftPoint.value.dy);
}
}
}),
),
const SizedBox(
width: 5,
),
Expanded(
child: TextFormField(
initialValue: controller.leftPoint.value.dy.toStringAsFixed(3),
keyboardType: TextInputType.number,
decoration: const InputDecoration(prefixText: 'y:'),
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入值';
}
return null;
},
onChanged: (String value) {
if (value != "") {
double point = double.parse(value);
if (point != 0) {
controller.leftPoint.value =
Offset(controller.leftPoint.value.dx, point);
}
}
}),
),
],
),
const Text("右侧位置(m):"),
Row(
children: [
Expanded(
child: TextFormField(
initialValue: controller.rightPoint.value.dx.toStringAsFixed(3),
keyboardType: TextInputType.number,
decoration: const InputDecoration(prefixText: 'x:'),
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入值';
}
return null;
},
onChanged: (String value) {
if (value != "") {
double point = double.parse(value);
if (point != 0) {
controller.rightPoint.value =
Offset(point, controller.rightPoint.value.dy);
}
}
}),
),
const SizedBox(
width: 5,
),
Expanded(
child: TextFormField(
initialValue: controller.rightPoint.value.dy.toStringAsFixed(3),
keyboardType: TextInputType.number,
decoration: const InputDecoration(prefixText: 'y:'),
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入值';
}
return null;
},
onChanged: (String value) {
if (value != "") {
double point = double.parse(value);
if (point != 0) {
controller.rightPoint.value =
Offset(controller.rightPoint.value.dx, point);
}
}
}),
)
],
),
];
Widget button = TextButton(
onPressed: () {
Offset center = controller.leftPoint.value;
Offset right =
controller.rightPoint.value - controller.leftPoint.value;
Connect().writeAntenna(
Offset(-center.dx, center.dy),
Offset(right.dx, -right.dy),
blueToothController.connectedDeviceID.value);
},
child: const Text("位置更新"));
return Scaffold(
appBar: AppBar(
title: const Text('天线位置设置'),
toolbarHeight: 40,
),
body: SingleChildScrollView(
child: isPortrait
? Column(
children: [
simpleDevice,
Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [...inputList, button],
),
)
],
)
: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
simpleDevice,
Expanded(
child: Container(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [...inputList, button],
),
))
],
),
),
);
}
}
//
class SimpleDevice extends CustomPainter {
final AntennaController controller;
final bool isDarkMode;
SimpleDevice(this.controller, this.isDarkMode) : super();
@override
void paint(Canvas canvas, Size size) {
final Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2.0;
paint.color = isDarkMode ? Colors.white : Colors.black;
final Path path = Path();
double w = size.width;
if (size.width > size.height) {
w = size.height;
}
double unit = w * .1;
double width = w * .2;
double height = w * .8;
double left = (size.width - width) / 2;
double top = (size.height - height) / 2;
canvas.translate(left, top);
canvas.drawRect(Rect.fromLTWH(0, 0, width, height), paint);
Offset center = Offset(controller.centerPoint.value.dx * unit,
controller.centerPoint.value.dy * unit);
double radius = 10;
canvas.drawCircle(center, radius, paint);
canvas.translate(center.dx, center.dy);
Offset leftPoint = Offset(controller.leftPoint.value.dx * unit,
controller.leftPoint.value.dy * unit);
Offset rightPoint = Offset(controller.rightPoint.value.dx * unit,
controller.rightPoint.value.dy * unit);
canvas.drawLine(const Offset(10, 0), rightPoint, paint);
canvas.drawLine(const Offset(-10, 0), leftPoint, paint);
canvas.drawCircle(rightPoint, 10, paint);
canvas.drawCircle(leftPoint, 10, paint);
canvas.translate(-center.dx, -center.dy);
canvas.drawPath(path, paint);
canvas.translate(-left, -top);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
class AntennaController extends GetxController {
var leftPoint = const Offset(-1, 3).obs;
var rightPoint = const Offset(1, 3).obs;
var centerPoint = const Offset(1.0, 0.5).obs;
}

View File

@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import '../login_in/service/service.dart';
import '../login_in/user/loginprefs.dart';
import '../login_in/user/userModel.dart';
import '../screens/setting_page.dart';
class PersonDetails extends StatefulWidget {
const PersonDetails({super.key});
@override
State<PersonDetails> createState() => _PersonDetailsState();
}
class _PersonDetailsState extends State<PersonDetails> {
String userName = '';
List<ListItem> items = [];
late UserModel user;
LoginPrefs loginPrefs = LoginPrefs();
@override
void initState() {
SchedulerBinding.instance.addPostFrameCallback((_) async {
UserModel? getUser = UserController().getUser();
if (getUser == null) {
var person = await GetServices().getPerson();
UserController().setUser(person);
user = UserModel.fromJson(person);
} else {}
setState(() {
user = UserController().getUser()!;
});
emptyFunc() => {};
items = [
// '用户名'
ListItem(user.username, Icons.home, emptyFunc),
// "姓名"
ListItem(user.name, Icons.person, emptyFunc),
// "昵称"
ListItem(user.nickName, Icons.person_add_rounded, emptyFunc),
// "手机号"
ListItem(user.phone, Icons.phone_android, emptyFunc),
// "邮箱"
ListItem(user.email, Icons.email, emptyFunc),
// "备注"
ListItem(user.remark, Icons.info_outline, emptyFunc),
ListItem('退出登录', Icons.logout, () {
loginPrefs.clearLogin();
Navigator.pushNamed(context, 'login');
}),
];
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("个人中心")),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return Column(
children: <Widget>[
SizedBox(
height: 40,
child: ListTileButton(
item: items[index],
)),
const Divider(), // 线
],
);
},
));
}
}

View File

@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import '../screens/setting_page.dart';
class PlanPoint extends StatefulWidget {
const PlanPoint({super.key});
@override
State<PlanPoint> createState() => _PlanPointState();
}
class _PlanPointState extends State<PlanPoint> {
List<ListItem> items = [];
@override
void initState() {
items = [
ListItem('下载', Icons.download, () {}),
ListItem('删除', Icons.delete, () {}),
];
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 40,
title: const Text("规划点"),
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return Column(
children: <Widget>[
ListTileButton(
item: items[index],
),
const Divider(), // 线
],
);
},
),
);
}
}

252
lib/setting/wifi_page.dart Normal file
View File

@ -0,0 +1,252 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:wifi_iot/wifi_iot.dart';
import 'dart:async';
import 'package:wifi_scan/wifi_scan.dart';
import '../login_in/connect/wifi.dart';
SocketSetting socket = SocketSetting();
class WifiPage extends StatefulWidget {
const WifiPage({super.key});
@override
State<WifiPage> createState() => _WifiPageState();
}
class _WifiPageState extends State<WifiPage> {
String ip = "192.168.4.1";
int port = 6000;
@override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) async {
await _getScannedResults(context);
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
}
reconnectToWifi(currentTitle, password, withInternet) async {
String ssid = currentTitle;
bool isConnected = await WiFiForIoTPlugin.isConnected();
if (isConnected) {
// WiFi连接
await WiFiForIoTPlugin.disconnect();
}
// WiFi网络
await WiFiForIoTPlugin.connect(ssid,
password: password,
security: NetworkSecurity.WPA,
withInternet: withInternet);
//
isConnected = await WiFiForIoTPlugin.isConnected();
if (isConnected) {
connectedssid = ssid;
setState(() {});
print("Successfully reconnected to WiFi network: $ssid");
} else {
print("Failed to reconnect to WiFi network: $ssid");
}
}
List<WiFiAccessPoint> accessPoints = <WiFiAccessPoint>[];
StreamSubscription<List<WiFiAccessPoint>>? subscription;
bool shouldCheckCan = true;
Future<bool> _canGetScannedResults(BuildContext context) async {
if (shouldCheckCan) {
// check if can-getScannedResults
final can = await WiFiScan.instance.canGetScannedResults();
// if can-not, then show error
if (can != CanGetScannedResults.yes) {
if (mounted) kShowSnackBar(context, "Cannot get scanned results: $can");
accessPoints = <WiFiAccessPoint>[];
return false;
}
}
return true;
}
Future<void> _getScannedResults(BuildContext context) async {
if (await _canGetScannedResults(context)) {
// get scanned results
final results = await WiFiScan.instance.getScannedResults();
setState(() => accessPoints = results);
}
}
bool withInternet = false;
String password = "01010101";
String currentTitle = "";
String connectedssid = "";
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
TextEditingController passWordController =
TextEditingController(text: password);
List<Widget> children = [
Row(
children: [
const Text("是否允许上网:"),
Switch(
value: withInternet,
onChanged: ((value) {
setState(() {
withInternet = value;
});
})),
],
),
TextFormField(
controller: passWordController,
obscureText: true,
decoration: const InputDecoration(
prefixText: '密码:',
border: InputBorder.none,
),
onChanged: (String value) {
password = value;
},
)
];
return Scaffold(
appBar: AppBar(
toolbarHeight: 40,
title: const Text('WiFi列表'),
actions: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: IconButton(
onPressed: () async => _getScannedResults(context),
icon: const Icon(Icons.refresh)),
)
],
),
body: Builder(
builder: (context) => Padding(
padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 20),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Divider(),
Flexible(
child: Center(
child: accessPoints.isEmpty
? const Text("")
: ListView.builder(
itemCount: accessPoints.length,
itemBuilder: (context, i) {
var accessPoint = accessPoints[i];
var signalIcon = accessPoint.level >= -80
? Icons.signal_wifi_4_bar
: Icons.signal_wifi_0_bar;
int level = accessPoint.level;
if (level <= -80) {
signalIcon = Icons.signal_wifi_0_bar; //
} else if (level > -80 && level <= -70) {
signalIcon = Icons.network_wifi_2_bar_sharp; //
} else if (level > -70 && level <= -60) {
signalIcon = Icons.network_wifi_3_bar_sharp; //
} else if (level > -60 && level <= -50) {
signalIcon = Icons.network_wifi; //
} else {
signalIcon = Icons.signal_wifi_4_bar; //
}
final title = accessPoint.ssid.isNotEmpty
? accessPoint.ssid
: "";
return ListTile(
visualDensity: VisualDensity.compact,
leading: Icon(signalIcon),
title: Text(title),
subtitle: Text(accessPoint.bssid),
trailing: ElevatedButton(
child: Text(
connectedssid == accessPoint.ssid &&
connectedssid != ""
? "已连接"
: '连接'),
onPressed: () {
currentTitle = title;
Scaffold.of(context).openEndDrawer();
}),
onTap: () {
currentTitle = title;
Scaffold.of(context).openEndDrawer();
},
);
}),
),
),
],
),
),
),
endDrawer: Drawer(
backgroundColor: isDarkMode ? Colors.black : Colors.white,
width: size.width * .8,
child: SingleChildScrollView(
child: Container(
height: size.height,
padding: const EdgeInsets.fromLTRB(5, 20, 5, 20),
child: Stack(
children: [
Column(
children: children,
),
Positioned(
bottom: 20,
left: 10,
child: Row(
children: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("取消")),
TextButton(
onPressed: () async {
Navigator.of(context).pop();
await reconnectToWifi(
currentTitle, password, withInternet);
},
child: const Text("确认"),
),
TextButton(
onPressed: () async {
Navigator.of(context).pop();
await socket.connect();
},
child: const Text("连接"),
)
],
))
],
),
),
),
),
);
}
}
void kShowSnackBar(BuildContext context, String message) {
if (kDebugMode) print(message);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(message)));
}

253
lib/setting/xy_change.dart Normal file
View File

@ -0,0 +1,253 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:get/get.dart';
import '../login_in/connect/blue_tooth.dart';
import '../login_in/connect/index.dart';
import '../login_in/getx/blue_tooth.dart';
import '../login_in/service/coor_trans.dart';
import '../login_in/service/service.dart';
import '../map_pointer/modules/common/device_type.dart';
final BlueToothController blueToothController = Get.put(BlueToothController());
BlueSetting blueSetting = BlueSetting();
class XyChangeView extends StatefulWidget {
const XyChangeView({super.key});
@override
State<XyChangeView> createState() => _XyChangeViewState();
}
class _XyChangeViewState extends State<XyChangeView> {
List<Widget> list = [];
CoorTransModel coorTrans = CoorTransModel(
name: "",
L0: 0,
belt: "",
dstEllipsoid: "",
dx: 0,
dy: 0,
dz: 0,
id: 0,
wx: 0,
wy: 0,
wz: 0,
k: 0,
elevation: 0,
isMain: 0,
rota: 0,
srcEllipsoid: "");
@override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) async {
List coordTransmap = await GetServices().getCoordTrans();
if (coordTransmap.isNotEmpty) {
var mianBelt =
coordTransmap.firstWhere((element) => element['belt'] == 'main');
coorTrans = CoorTransModel.fromJson(mianBelt);
setState(() {});
} else {
print("当前项目没有设置中央子午线");
return;
}
});
}
@override
Widget build(BuildContext context) {
final deviceType = getDeviceType(context);
final size = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
title: const Text("坐标转换"),
toolbarHeight: 40,
),
body: Container(
margin: const EdgeInsets.all(5),
child: ListView.builder(
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
TextEditingController l0Controller =
TextEditingController(text: coorTrans.L0.toString());
TextEditingController elevationController =
TextEditingController(text: coorTrans.elevation.toString());
TextEditingController dxController =
TextEditingController(text: coorTrans.dx.toString());
TextEditingController dyController =
TextEditingController(text: coorTrans.dy.toString());
TextEditingController dzController =
TextEditingController(text: coorTrans.dz.toString());
TextEditingController wxController =
TextEditingController(text: coorTrans.wx.toString());
TextEditingController wyController =
TextEditingController(text: coorTrans.wy.toString());
TextEditingController wzController =
TextEditingController(text: coorTrans.wz.toString());
TextEditingController kController =
TextEditingController(text: coorTrans.k.toString());
List<Widget> list1 = [
Flexible(
child: TextFormField(
keyboardType: TextInputType.number,
controller: l0Controller,
decoration: const InputDecoration(
prefixText: '中央子午线:',
border: InputBorder.none,
),
onChanged: (String value) {
// coorTrans.L0 =(value);
},
)),
const Divider(),
Flexible(
child: TextFormField(
keyboardType: TextInputType.number,
controller: elevationController,
decoration: const InputDecoration(
border: InputBorder.none,
prefixText: '投影高程:',
),
onChanged: (String value) {
// coorTrans.L0 =(value);
},
)),
];
List<Widget> list2 = [
Flexible(
child: TextFormField(
keyboardType: TextInputType.number,
controller: dxController,
decoration: const InputDecoration(
border: InputBorder.none,
prefixText: 'x平移(m):',
),
onChanged: (String value) {
// coorTrans.L0 =(value);
},
)),
const Divider(),
Flexible(
child: TextFormField(
keyboardType: TextInputType.number,
controller: dyController,
decoration: const InputDecoration(
prefixText: 'Y平移(米):',
border: InputBorder.none,
),
onChanged: (String value) {
// coorTrans.L0 =(value);
},
)),
const Divider(),
Flexible(
child: TextFormField(
keyboardType: TextInputType.number,
controller: dzController,
decoration: const InputDecoration(
prefixText: 'Z平移(米):',
border: InputBorder.none,
),
onChanged: (String value) {
// coorTrans.L0 =(value);
},
)),
const Divider(),
];
List<Widget> list3 = [
Flexible(
child: TextFormField(
keyboardType: TextInputType.number,
controller: wxController,
decoration: const InputDecoration(
border: InputBorder.none,
prefixText: 'X轴旋转(秒):',
),
onChanged: (String value) {
// coorTrans.L0 =(value);
},
)),
const Divider(),
Flexible(
child: TextFormField(
keyboardType: TextInputType.number,
controller: wyController,
decoration: const InputDecoration(
border: InputBorder.none,
prefixText: 'Y轴旋转(秒):',
),
onChanged: (String value) {
// coorTrans.L0 =(value);
},
)),
const Divider(),
Flexible(
child: TextFormField(
keyboardType: TextInputType.number,
controller: wzController,
decoration: const InputDecoration(
border: InputBorder.none,
prefixText: 'Z轴旋转(秒):',
),
onChanged: (String value) {
// coorTrans.L0 =(value);
},
)),
const Divider(),
];
return Column(
children: <Widget>[
deviceType == DeviceType.mobile && size.width < 350
? Column(
children: list1,
)
: Row(
children: list1,
),
const Divider(),
deviceType == DeviceType.mobile && size.width < 350
? Column(
children: list2,
)
: Row(children: list2),
const Divider(),
deviceType == DeviceType.mobile && size.width < 350
? Column(
children: list3,
)
: Row(
children: list3,
),
const Divider(),
SizedBox(
child: TextFormField(
keyboardType: TextInputType.number,
controller: kController,
decoration: const InputDecoration(
border: InputBorder.none,
prefixText: '尺度:',
),
onChanged: (String value) {
// coorTrans.L0 =(value);
},
)),
const Divider(),
TextButton(
onPressed: () {
Connect().writeCoorTransValue(
blueToothController.connectedDeviceID.value,
coorTrans);
},
child: const Text("更新设置"))
],
);
},
),
));
}
}

View File

@ -7,8 +7,10 @@ import Foundation
import flutter_webrtc
import path_provider_foundation
import quick_blue
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
QuickBluePlugin.register(with: registry.registrar(forPlugin: "QuickBluePlugin"))
}

29
plugins/quick_blue/.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.packages
build/

View File

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 77d935af4db863f6abd0b9c31c7e6df2a13de57b
channel: stable
project_type: plugin

View File

@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

View File

@ -0,0 +1 @@
TODO: Add your license here.

View File

@ -0,0 +1,109 @@
# quick_blue
A cross-platform (Android/iOS/macOS/Windows/Linux) BluetoothLE plugin for Flutter
# Usage
- [Receive BLE availability changes](#receive-ble-availability-changes)
- [Scan BLE peripheral](#scan-ble-peripheral)
- [Connect BLE peripheral](#connect-ble-peripheral)
- [Discover services of BLE peripheral](#discover-services-of-ble-peripheral)
- [Transfer data between BLE central & peripheral](#transfer-data-between-ble-central--peripheral)
| API | Android | iOS | macOS | Windows | Linux |
| :--- | :---: | :---: | :---: | :---: | :---: |
| availabilityChangeStream | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| isBluetoothAvailable | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| startScan/stopScan | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| connect/disconnect | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| discoverServices | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| setNotifiable | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| readValue | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| writeValue | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| requestMtu | ✔️ | ✔️ | ✔️ | ✔️ | |
> * Windows' APIs are little different on `discoverServices`: https://github.com/woodemi/quick_blue/issues/76
## Receive BLE availability changes
iOS/macOS
```dart
QuickBlue.availabilityChangeStream.listen((state) {
debugPrint('Bluetooth state: ${state.toString()}');
});
```
## Scan BLE peripheral
Android/iOS/macOS/Windows/Linux
```dart
QuickBlue.scanResultStream.listen((result) {
print('onScanResult $result');
});
QuickBlue.startScan();
// ...
QuickBlue.stopScan();
```
## Connect BLE peripheral
Connect to `deviceId`, received from `QuickBlue.scanResultStream`
```dart
QuickBlue.setConnectionHandler(_handleConnectionChange);
void _handleConnectionChange(String deviceId, BlueConnectionState state) {
print('_handleConnectionChange $deviceId, $state');
}
QuickBlue.connect(deviceId);
// ...
QuickBlue.disconnect(deviceId);
```
## Discover services of BLE peripheral
Discover services od `deviceId`
```dart
QuickBlue.setServiceHandler(_handleServiceDiscovery);
void _handleServiceDiscovery(String deviceId, String serviceId) {
print('_handleServiceDiscovery $deviceId, $serviceId');
}
QuickBlue.discoverServices(deviceId);
```
## Transfer data between BLE central & peripheral
- Pull data from peripheral of `deviceId`
> Data would receive within value handler of `QuickBlue.setValueHandler`
> Because it is how [peripheral(_:didUpdateValueFor:error:)](https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518708-peripheral) work on iOS/macOS
```dart
// Data would receive from value handler of `QuickBlue.setValueHandler`
QuickBlue.readValue(deviceId, serviceId, characteristicId);
```
- Send data to peripheral of `deviceId`
```dart
QuickBlue.writeValue(deviceId, serviceId, characteristicId, value);
```
- Receive data from peripheral of `deviceId`
```dart
QuickBlue.setValueHandler(_handleValueChange);
void _handleValueChange(String deviceId, String characteristicId, Uint8List value) {
print('_handleValueChange $deviceId, $characteristicId, ${hex.encode(value)}');
}
QuickBlue.setNotifiable(deviceId, serviceId, characteristicId, true);
```

View File

@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

8
plugins/quick_blue/android/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures

View File

@ -0,0 +1,50 @@
group 'com.example.quick_blue'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.5.30'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 30
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
minSdkVersion 21
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

View File

@ -0,0 +1 @@
rootProject.name = 'quick_blue'

View File

@ -0,0 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.quick_blue">
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- legacy for Android 11 or lower -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- legacy for Android 9 or lower -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>

View File

@ -0,0 +1,349 @@
package com.example.quick_blue
import android.annotation.SuppressLint
import android.bluetooth.*
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.content.Context
import android.content.BroadcastReceiver
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.*
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.*
private const val TAG = "QuickBluePlugin"
/** QuickBluePlugin */
@SuppressLint("MissingPermission")
class QuickBluePlugin: FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var method : MethodChannel
private lateinit var eventAvailabilityChange : EventChannel
private lateinit var eventScanResult : EventChannel
private lateinit var messageConnector: BasicMessageChannel<Any>
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
method = MethodChannel(flutterPluginBinding.binaryMessenger, "quick_blue/method")
eventAvailabilityChange = EventChannel(flutterPluginBinding.binaryMessenger, "quick_blue/event.availabilityChange")
eventScanResult = EventChannel(flutterPluginBinding.binaryMessenger, "quick_blue/event.scanResult")
messageConnector = BasicMessageChannel(flutterPluginBinding.binaryMessenger, "quick_blue/message.connector", StandardMessageCodec.INSTANCE)
method.setMethodCallHandler(this)
eventAvailabilityChange.setStreamHandler(this)
eventScanResult.setStreamHandler(this)
context = flutterPluginBinding.applicationContext
mainThreadHandler = Handler(Looper.getMainLooper())
bluetoothManager = flutterPluginBinding.applicationContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
context.registerReceiver(
broadcastReceiver,
IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
)
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
bluetoothManager.adapter.bluetoothLeScanner?.stopScan(scanCallback)
context.unregisterReceiver(broadcastReceiver)
eventAvailabilityChange.setStreamHandler(null)
eventScanResult.setStreamHandler(null)
method.setMethodCallHandler(null)
}
private lateinit var context: Context
private lateinit var mainThreadHandler: Handler
private lateinit var bluetoothManager: BluetoothManager
private val knownGatts = mutableListOf<BluetoothGatt>()
private fun sendMessage(messageChannel: BasicMessageChannel<Any>, message: Map<String, Any>) {
mainThreadHandler.post { messageChannel.send(message) }
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) {
"isBluetoothAvailable" -> {
result.success(bluetoothManager.adapter.isEnabled)
}
"startScan" -> {
bluetoothManager.adapter.bluetoothLeScanner?.startScan(scanCallback)
result.success(null)
}
"stopScan" -> {
bluetoothManager.adapter.bluetoothLeScanner?.stopScan(scanCallback)
result.success(null)
}
"connect" -> {
val deviceId = call.argument<String>("deviceId")!!
if (knownGatts.find { it.device.address == deviceId } != null) {
return result.success(null)
}
val remoteDevice = bluetoothManager.adapter.getRemoteDevice(deviceId)
val gatt = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
remoteDevice.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
} else {
remoteDevice.connectGatt(context, false, gattCallback)
}
knownGatts.add(gatt)
result.success(null)
// TODO connecting
}
"disconnect" -> {
val deviceId = call.argument<String>("deviceId")!!
val gatt = knownGatts.find { it.device.address == deviceId }
?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", null)
cleanConnection(gatt)
result.success(null)
//FIXME If `disconnect` is called before BluetoothGatt.STATE_CONNECTED
// there will be no `disconnected` message any more
}
"discoverServices" -> {
val deviceId = call.argument<String>("deviceId")!!
val gatt = knownGatts.find { it.device.address == deviceId }
?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", null)
gatt.discoverServices()
result.success(null)
}
"setNotifiable" -> {
val deviceId = call.argument<String>("deviceId")!!
val service = call.argument<String>("service")!!
val characteristic = call.argument<String>("characteristic")!!
val bleInputProperty = call.argument<String>("bleInputProperty")!!
val gatt = knownGatts.find { it.device.address == deviceId }
?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", null)
val c = gatt.getCharacteristic(service, characteristic)
?: return result.error("IllegalArgument", "Unknown characteristic: $characteristic", null)
gatt.setNotifiable(c, bleInputProperty)
result.success(null)
}
"readValue" -> {
val deviceId = call.argument<String>("deviceId")!!
val service = call.argument<String>("service")!!
val characteristic = call.argument<String>("characteristic")!!
val gatt = knownGatts.find { it.device.address == deviceId }
?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", null)
val c = gatt.getCharacteristic(service, characteristic)
?: return result.error("IllegalArgument", "Unknown characteristic: $characteristic", null)
if (gatt.readCharacteristic(c))
result.success(null)
else
result.error("Characteristic unavailable", null, null)
}
"writeValue" -> {
val deviceId = call.argument<String>("deviceId")!!
val service = call.argument<String>("service")!!
val characteristic = call.argument<String>("characteristic")!!
val value = call.argument<ByteArray>("value")!!
val gatt = knownGatts.find { it.device.address == deviceId }
?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", null)
val c = gatt.getCharacteristic(service, characteristic)
?: return result.error("IllegalArgument", "Unknown characteristic: $characteristic", null)
c.value = value
if (gatt.writeCharacteristic(c))
result.success(null)
else
result.error("Characteristic unavailable", null, null)
}
"requestMtu" -> {
val deviceId = call.argument<String>("deviceId")!!
val expectedMtu = call.argument<Int>("expectedMtu")!!
val gatt = knownGatts.find { it.device.address == deviceId }
?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", null)
gatt.requestMtu(expectedMtu)
result.success(null)
}
else -> {
result.notImplemented()
}
}
}
private fun cleanConnection(gatt: BluetoothGatt) {
knownGatts.remove(gatt)
gatt.disconnect()
}
enum class AvailabilityState(val value: Int) {
unknown(0),
resetting(1),
unsupported(2),
unauthorized(3),
poweredOff(4),
poweredOn(5),
}
fun BluetoothManager.getAvailabilityState(): AvailabilityState {
val state = adapter?.state ?: return AvailabilityState.unsupported
return when(state) {
BluetoothAdapter.STATE_OFF -> AvailabilityState.poweredOff
BluetoothAdapter.STATE_ON -> AvailabilityState.poweredOn
BluetoothAdapter.STATE_TURNING_ON -> AvailabilityState.resetting
BluetoothAdapter.STATE_TURNING_OFF -> AvailabilityState.resetting
else -> AvailabilityState.unknown
}
}
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent?.action == BluetoothAdapter.ACTION_STATE_CHANGED) {
availabilityChangeSink?.success(bluetoothManager.getAvailabilityState().value)
}
}
}
private val scanCallback = object : ScanCallback() {
override fun onScanFailed(errorCode: Int) {
Log.v(TAG, "onScanFailed: $errorCode")
}
override fun onScanResult(callbackType: Int, result: ScanResult) {
// Log.v(TAG, "onScanResult: $callbackType + $result")
scanResultSink?.success(mapOf<String, Any>(
"name" to (result.device.name ?: ""),
"deviceId" to result.device.address,
"manufacturerDataHead" to (result.manufacturerDataHead ?: byteArrayOf()),
"rssi" to result.rssi
))
}
override fun onBatchScanResults(results: MutableList<ScanResult>?) {
Log.v(TAG, "onBatchScanResults: $results")
}
}
private var availabilityChangeSink: EventChannel.EventSink? = null
private var scanResultSink: EventChannel.EventSink? = null
override fun onListen(args: Any?, eventSink: EventChannel.EventSink?) {
val map = args as? Map<String, Any> ?: return
when (map["name"]) {
"availabilityChange" -> {
availabilityChangeSink = eventSink
availabilityChangeSink?.success(bluetoothManager.getAvailabilityState().value)
}
"scanResult" -> scanResultSink = eventSink
}
}
override fun onCancel(args: Any?) {
val map = args as? Map<String, Any> ?: return
when (map["name"]) {
"availabilityChange" -> availabilityChangeSink = null
"scanResult" -> scanResultSink = null
}
}
private val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
Log.v(TAG, "onConnectionStateChange: device(${gatt.device.address}) status($status), newState($newState)")
if (newState == BluetoothGatt.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) {
sendMessage(messageConnector, mapOf(
"deviceId" to gatt.device.address,
"ConnectionState" to "connected"
))
} else {
cleanConnection(gatt)
sendMessage(messageConnector, mapOf(
"deviceId" to gatt.device.address,
"ConnectionState" to "disconnected"
))
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
Log.v(TAG, "onServicesDiscovered ${gatt.device.address} $status")
if (status != BluetoothGatt.GATT_SUCCESS) return
gatt.services?.forEach { service ->
Log.v(TAG, "Service " + service.uuid)
service.characteristics.forEach { characteristic ->
Log.v(TAG, " Characteristic ${characteristic.uuid}")
characteristic.descriptors.forEach {
Log.v(TAG, " Descriptor ${it.uuid}")
}
}
sendMessage(messageConnector, mapOf(
"deviceId" to gatt.device.address,
"ServiceState" to "discovered",
"service" to service.uuid.toString(),
"characteristics" to service.characteristics.map { it.uuid.toString() }
))
}
}
override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
if (status == BluetoothGatt.GATT_SUCCESS) {
sendMessage(messageConnector, mapOf(
"mtuConfig" to mtu
))
}
}
override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
Log.v(TAG, "onCharacteristicRead ${characteristic.uuid}, ${characteristic.value.contentToString()}")
sendMessage(messageConnector, mapOf(
"deviceId" to gatt.device.address,
"characteristicValue" to mapOf(
"characteristic" to characteristic.uuid.toString(),
"value" to characteristic.value
)
))
}
override fun onCharacteristicWrite(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic, status: Int) {
Log.v(TAG, "onCharacteristicWrite ${characteristic.uuid}, ${characteristic.value.contentToString()} $status")
}
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
Log.v(TAG, "onCharacteristicChanged ${characteristic.uuid}, ${characteristic.value.contentToString()}")
sendMessage(messageConnector, mapOf(
"deviceId" to gatt.device.address,
"characteristicValue" to mapOf(
"characteristic" to characteristic.uuid.toString(),
"value" to characteristic.value
)
))
}
}
}
val ScanResult.manufacturerDataHead: ByteArray?
get() {
val sparseArray = scanRecord?.manufacturerSpecificData ?: return null
if (sparseArray.size() == 0) return null
return sparseArray.keyAt(0).toShort().toByteArray() + sparseArray.valueAt(0)
}
fun Short.toByteArray(byteOrder: ByteOrder = ByteOrder.LITTLE_ENDIAN): ByteArray =
ByteBuffer.allocate(2 /*Short.SIZE_BYTES*/).order(byteOrder).putShort(this).array()
fun BluetoothGatt.getCharacteristic(service: String, characteristic: String): BluetoothGattCharacteristic? =
getService(UUID.fromString(service)).getCharacteristic(UUID.fromString(characteristic))
private val DESC__CLIENT_CHAR_CONFIGURATION = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
fun BluetoothGatt.setNotifiable(gattCharacteristic: BluetoothGattCharacteristic, bleInputProperty: String) {
val descriptor = gattCharacteristic.getDescriptor(DESC__CLIENT_CHAR_CONFIGURATION)
val (value, enable) = when (bleInputProperty) {
"notification" -> BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE to true
"indication" -> BluetoothGattDescriptor.ENABLE_INDICATION_VALUE to true
else -> BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE to false
}
descriptor.value = value
setCharacteristicNotification(descriptor.characteristic, enable) && writeDescriptor(descriptor)
}

View File

@ -0,0 +1,281 @@
import CoreBluetooth
#if os(iOS)
import Flutter
import UIKit
#elseif os(OSX)
import Cocoa
import FlutterMacOS
#endif
let GATT_HEADER_LENGTH = 3
let GSS_SUFFIX = "0000-1000-8000-00805f9b34fb"
extension CBUUID {
public var uuidStr: String {
get {
uuidString.lowercased()
}
}
}
extension CBPeripheral {
// FIXME https://forums.developer.apple.com/thread/84375
public var uuid: UUID {
get {
value(forKey: "identifier") as! NSUUID as UUID
}
}
public func getCharacteristic(_ characteristic: String, of service: String) -> CBCharacteristic? {
let s = self.services?.first {
$0.uuid.uuidStr == service || "0000\($0.uuid.uuidStr)-\(GSS_SUFFIX)" == service
}
let c = s?.characteristics?.first {
$0.uuid.uuidStr == characteristic || "0000\($0.uuid.uuidStr)-\(GSS_SUFFIX)" == characteristic
}
return c
}
public func setNotifiable(_ bleInputProperty: String, for characteristic: String, of service: String) {
guard let characteristic = getCharacteristic(characteristic, of: service) else{
return
}
setNotifyValue(bleInputProperty != "disabled", for: characteristic)
}
}
public class QuickBlueDarwin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
#if os(iOS)
let messenger = registrar.messenger()
#elseif os(OSX)
let messenger = registrar.messenger
#endif
let method = FlutterMethodChannel(name: "quick_blue/method", binaryMessenger: messenger)
let eventAvailabilityChange = FlutterEventChannel(name: "quick_blue/event.availabilityChange", binaryMessenger: messenger)
let eventScanResult = FlutterEventChannel(name: "quick_blue/event.scanResult", binaryMessenger: messenger)
let messageConnector = FlutterBasicMessageChannel(name: "quick_blue/message.connector", binaryMessenger: messenger)
let instance = QuickBlueDarwin()
registrar.addMethodCallDelegate(instance, channel: method)
eventAvailabilityChange.setStreamHandler(instance)
eventScanResult.setStreamHandler(instance)
instance.messageConnector = messageConnector
}
private lazy var manager: CBCentralManager = { CBCentralManager(delegate: self, queue: nil) }()
private var discoveredPeripherals = Dictionary<String, CBPeripheral>()
private var availabilityChangeSink: FlutterEventSink?
private var scanResultSink: FlutterEventSink?
private var messageConnector: FlutterBasicMessageChannel!
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "isBluetoothAvailable":
result(manager.state == .poweredOn)
case "startScan":
manager.scanForPeripherals(withServices: nil)
result(nil)
case "stopScan":
manager.stopScan()
result(nil)
case "connect":
let arguments = call.arguments as! Dictionary<String, Any>
let deviceId = arguments["deviceId"] as! String
guard let peripheral = discoveredPeripherals[deviceId] else {
result(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(deviceId)", details: nil))
return
}
peripheral.delegate = self
manager.connect(peripheral)
result(nil)
case "disconnect":
let arguments = call.arguments as! Dictionary<String, Any>
let deviceId = arguments["deviceId"] as! String
guard let peripheral = discoveredPeripherals[deviceId] else {
result(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(deviceId)", details: nil))
return
}
if (peripheral.state != .disconnected) {
manager.cancelPeripheralConnection(peripheral)
}
result(nil)
case "discoverServices":
let arguments = call.arguments as! Dictionary<String, Any>
let deviceId = arguments["deviceId"] as! String
guard let peripheral = discoveredPeripherals[deviceId] else {
result(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(deviceId)", details: nil))
return
}
peripheral.discoverServices(nil)
result(nil)
case "setNotifiable":
let arguments = call.arguments as! Dictionary<String, Any>
let deviceId = arguments["deviceId"] as! String
let service = arguments["service"] as! String
let characteristic = arguments["characteristic"] as! String
let bleInputProperty = arguments["bleInputProperty"] as! String
guard let peripheral = discoveredPeripherals[deviceId] else {
result(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(deviceId)", details: nil))
return
}
guard let c = peripheral.getCharacteristic(characteristic, of: service) else {
result(FlutterError(code: "IllegalArgument", message: "Unknown characteristic:\(characteristic)", details: nil))
return
}
peripheral.setNotifyValue(bleInputProperty != "disabled", for: c)
result(nil)
case "readValue":
let arguments = call.arguments as! Dictionary<String, Any>
let deviceId = arguments["deviceId"] as! String
let service = arguments["service"] as! String
let characteristic = arguments["characteristic"] as! String
guard let peripheral = discoveredPeripherals[deviceId] else {
result(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(deviceId)", details: nil))
return
}
guard let c = peripheral.getCharacteristic(characteristic, of: service) else {
result(FlutterError(code: "IllegalArgument", message: "Unknown characteristic:\(characteristic)", details: nil))
return
}
peripheral.readValue(for: c)
result(nil)
case "writeValue":
let arguments = call.arguments as! Dictionary<String, Any>
let deviceId = arguments["deviceId"] as! String
let service = arguments["service"] as! String
let characteristic = arguments["characteristic"] as! String
let value = arguments["value"] as! FlutterStandardTypedData
let bleOutputProperty = arguments["bleOutputProperty"] as! String
guard let peripheral = discoveredPeripherals[deviceId] else {
result(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(deviceId)", details: nil))
return
}
let type = bleOutputProperty == "withoutResponse" ? CBCharacteristicWriteType.withoutResponse : CBCharacteristicWriteType.withResponse
guard let c = peripheral.getCharacteristic(characteristic, of: service) else {
result(FlutterError(code: "IllegalArgument", message: "Unknown characteristic:\(characteristic)", details: nil))
return
}
peripheral.writeValue(value.data, for: c, type: type)
result(nil)
case "requestMtu":
let arguments = call.arguments as! Dictionary<String, Any>
let deviceId = arguments["deviceId"] as! String
guard let peripheral = discoveredPeripherals[deviceId] else {
result(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(deviceId)", details: nil))
return
}
result(nil)
let mtu = peripheral.maximumWriteValueLength(for: .withoutResponse)
print("peripheral.maximumWriteValueLengthForType:CBCharacteristicWriteWithoutResponse \(mtu)")
messageConnector.sendMessage(["mtuConfig": mtu + GATT_HEADER_LENGTH])
default:
result(FlutterMethodNotImplemented)
}
}
}
extension QuickBlueDarwin: CBCentralManagerDelegate {
public func centralManagerDidUpdateState(_ central: CBCentralManager) {
availabilityChangeSink?(central.state.rawValue)
}
public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
print("centralManager:didDiscoverPeripheral \(peripheral.name ?? "nil") \(peripheral.uuid.uuidString)")
discoveredPeripherals[peripheral.uuid.uuidString] = peripheral
let manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data
scanResultSink?([
"name": peripheral.name ?? "",
"deviceId": peripheral.uuid.uuidString,
"manufacturerData": FlutterStandardTypedData(bytes: manufacturerData ?? Data()),
"rssi": RSSI,
])
}
public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("centralManager:didConnect \(peripheral.uuid.uuidString)")
messageConnector.sendMessage([
"deviceId": peripheral.uuid.uuidString,
"ConnectionState": "connected",
])
}
public func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("centralManager:didDisconnectPeripheral: \(peripheral.uuid.uuidString) error: \(String(describing: error))")
messageConnector.sendMessage([
"deviceId": peripheral.uuid.uuidString,
"ConnectionState": "disconnected",
])
}
}
extension QuickBlueDarwin: FlutterStreamHandler {
open func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
guard let args = arguments as? Dictionary<String, Any>, let name = args["name"] as? String else {
return nil
}
print("QuickBlueDarwin onListenWithArguments: \(name)")
if name == "availabilityChange" {
availabilityChangeSink = events
availabilityChangeSink?(manager.state.rawValue) // Initializes CBCentralManager and returns the current state when hot restarting
} else if name == "scanResult" {
scanResultSink = events
}
return nil
}
open func onCancel(withArguments arguments: Any?) -> FlutterError? {
guard let args = arguments as? Dictionary<String, Any>, let name = args["name"] as? String else {
return nil
}
print("QuickBlueDarwin onCancelWithArguments: \(name)")
if name == "availabilityChange" {
availabilityChangeSink = nil
} else if name == "scanResult" {
scanResultSink = nil
}
return nil
}
}
extension QuickBlueDarwin: CBPeripheralDelegate {
public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("peripheral: \(peripheral.uuid.uuidString) didDiscoverServices error: \(String(describing: error))")
for service in peripheral.services! {
peripheral.discoverCharacteristics(nil, for: service)
}
}
public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
print("peripheral:didDiscoverCharacteristicsForService (\(service.uuid.uuidStr), \(characteristic.uuid.uuidStr)")
}
self.messageConnector.sendMessage([
"deviceId": peripheral.uuid.uuidString,
"ServiceState": "discovered",
"service": service.uuid.uuidStr,
"characteristics": service.characteristics!.map { $0.uuid.uuidStr }
])
}
public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
let data = characteristic.value as NSData?
print("peripheral:didWriteValueForCharacteristic \(characteristic.uuid.uuidStr) \(String(describing: data)) error: \(String(describing: error))")
}
public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
let data = characteristic.value as NSData?
print("peripheral:didUpdateValueForCharacteristic \(characteristic.uuid) \(String(describing: data)) error: \(String(describing: error))")
self.messageConnector.sendMessage([
"deviceId": peripheral.uuid.uuidString,
"characteristicValue": [
"characteristic": characteristic.uuid.uuidStr,
"value": FlutterStandardTypedData(bytes: characteristic.value!)
]
])
}
}

46
plugins/quick_blue/example/.gitignore vendored Normal file
View File

@ -0,0 +1,46 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

View File

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 77d935af4db863f6abd0b9c31c7e6df2a13de57b
channel: stable
project_type: app

View File

@ -0,0 +1,16 @@
# quick_blue_example
Demonstrates how to use the quick_blue plugin.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@ -0,0 +1,13 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks

View File

@ -0,0 +1,68 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion flutter.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.quick_blue_example"
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.quick_blue_example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -0,0 +1,36 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.quick_blue_example">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:label="quick_blue_example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>

View File

@ -0,0 +1,6 @@
package com.example.quick_blue_example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.quick_blue_example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -0,0 +1,31 @@
buildscript {
ext.kotlin_version = '1.5.30'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true

View File

@ -0,0 +1,6 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip

View File

@ -0,0 +1,11 @@
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

View File

@ -0,0 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
</dict>
</plist>

View File

@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

View File

@ -0,0 +1,41 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

View File

@ -0,0 +1,552 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
B9073778C60E98C353E95558 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 267704C746F7481BE2FD075F /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
267704C746F7481BE2FD075F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
62DFED7748A45B2C8AB309AD /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
8D98FEFF83B7209CB5E85C69 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
928BCE08B84E5EF4963AE926 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B9073778C60E98C353E95558 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2B5912B4332ACFF87993A81F /* Frameworks */ = {
isa = PBXGroup;
children = (
267704C746F7481BE2FD075F /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
8E9EBB99EC94F35D1070368A /* Pods */ = {
isa = PBXGroup;
children = (
8D98FEFF83B7209CB5E85C69 /* Pods-Runner.debug.xcconfig */,
62DFED7748A45B2C8AB309AD /* Pods-Runner.release.xcconfig */,
928BCE08B84E5EF4963AE926 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
8E9EBB99EC94F35D1070368A /* Pods */,
2B5912B4332ACFF87993A81F /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
1A400B5A75970CECD3788202 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
4360CC4FBC1B3F978A10BABA /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
1A400B5A75970CECD3788202 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
4360CC4FBC1B3F978A10BABA /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = X637F4W694;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.quickBlueExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = X637F4W694;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.quickBlueExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = X637F4W694;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.quickBlueExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,13 @@
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View File

@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Some files were not shown because too many files have changed in this diff Show More