实时历史设置
@ -1,8 +1,17 @@
|
||||
# This is a generated file; do not edit or check into version control.
|
||||
flutter_webrtc=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\flutter_webrtc-0.9.48+hotfix.1\\
|
||||
libserialport=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\git\\libserialport-d6d27ef7aaf89e4dfa026db0c50dd3053c546710\\
|
||||
open_settings=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\open_settings-2.0.2\\
|
||||
path_provider=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\path_provider-2.1.4\\
|
||||
path_provider_android=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\path_provider_android-2.2.10\\
|
||||
path_provider_foundation=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\path_provider_foundation-2.4.0\\
|
||||
path_provider_linux=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\path_provider_linux-2.2.1\\
|
||||
path_provider_windows=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\path_provider_windows-2.3.0\\
|
||||
permission_handler=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\permission_handler-11.3.1\\
|
||||
permission_handler_android=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\permission_handler_android-12.0.12\\
|
||||
permission_handler_apple=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\permission_handler_apple-9.4.5\\
|
||||
permission_handler_html=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\permission_handler_html-0.1.3+2\\
|
||||
permission_handler_windows=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\permission_handler_windows-0.2.1\\
|
||||
quick_blue=D:\\workspace\\pile_NAV_new\\plugins\\quick_blue\\
|
||||
wifi_iot=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\wifi_iot-0.3.19+1\\
|
||||
wifi_scan=C:\\Users\\Administrator\\AppData\\Local\\Pub\\Cache\\hosted\\pub.dev\\wifi_scan-0.4.1+1\\
|
||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:scence_map/controllers/controller.dart';
|
||||
|
||||
import 'main.dart';
|
||||
import 'pages/aim_point/aimPointer.dart';
|
||||
|
||||
// import '../login_in/connect/bluetooth_page.dart';
|
||||
@ -56,10 +57,10 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
actions: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
isDarkMode.value = !isDarkMode.value;
|
||||
MyHomePage.isDarkMode.value = !MyHomePage.isDarkMode.value;
|
||||
},
|
||||
child: Icon(
|
||||
isDarkMode.value ? Icons.dark_mode : Icons.sunny,
|
||||
MyHomePage.isDarkMode.value ? Icons.dark_mode : Icons.sunny,
|
||||
size: 35,
|
||||
),
|
||||
),
|
||||
@ -74,7 +75,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
child: Image(
|
||||
image: const AssetImage('images/satellite.png'),
|
||||
// width: 40,
|
||||
color: isDarkMode.value
|
||||
color: MyHomePage.isDarkMode.value
|
||||
? Colors.white70
|
||||
: const Color.fromARGB(200, 29, 28, 28),
|
||||
height: 40,
|
||||
@ -99,7 +100,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
color: sightcontroller.isCardVisible.value
|
||||
? Colors.blue
|
||||
// : const Color.fromARGB(200, 29, 28, 28),
|
||||
: (isDarkMode.value
|
||||
: (MyHomePage.isDarkMode.value
|
||||
? Colors.white70
|
||||
: const Color.fromARGB(200, 29, 28, 28)),
|
||||
), // 新增图标
|
||||
@ -120,7 +121,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
size: 35,
|
||||
color: isDataVisible.value
|
||||
? Colors.blue
|
||||
: (isDarkMode.value
|
||||
: (MyHomePage.isDarkMode.value
|
||||
? Colors.white70
|
||||
: const Color.fromARGB(200, 29, 28, 28)),
|
||||
), // 新增图标
|
||||
@ -137,7 +138,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
child: Icon(
|
||||
Icons.settings_outlined,
|
||||
size: 35,
|
||||
color: isDarkMode.value
|
||||
color: MyHomePage.isDarkMode.value
|
||||
? Colors.white70
|
||||
: const Color.fromARGB(200, 29, 28, 28),
|
||||
),
|
||||
|
@ -6,16 +6,18 @@ import 'package:scence_map/controllers/controller.dart';
|
||||
import 'package:scence_map/controllers/plumController.dart';
|
||||
import 'appbar.dart';
|
||||
import 'pages/aim_point/aimPointer.dart';
|
||||
import 'pages/history/history_record.dart';
|
||||
import 'pages/pass_track/view.dart';
|
||||
import 'pages/pile/pileNav/view.dart';
|
||||
import 'pages/pile/rightDra/pileGenerate.dart';
|
||||
import 'pages/real/index.dart';
|
||||
import 'pages/setting/setting_page.dart';
|
||||
|
||||
void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); //设置全屏
|
||||
Get.put(ScenceMapController());
|
||||
Get.put(PlumDataController());
|
||||
|
||||
Get.put(SightController());
|
||||
Get.put(GnssController());
|
||||
runApp(const MyApp());
|
||||
@ -23,50 +25,25 @@ void main() {
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: MyHomePage.isDarkMode,
|
||||
builder: (context, isDarkMode, child) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
// This is the theme of your application.
|
||||
//
|
||||
// TRY THIS: Try running your application with "flutter run". You'll see
|
||||
// the application has a purple toolbar. Then, without quitting the app,
|
||||
// try changing the seedColor in the colorScheme below to Colors.green
|
||||
// and then invoke "hot reload" (save your changes or press the "hot
|
||||
// reload" button in a Flutter-supported IDE, or press "r" if you used
|
||||
// the command line to start the app).
|
||||
//
|
||||
// Notice that the counter didn't reset back to zero; the application
|
||||
// state is not lost during the reload. To reset the state, use hot
|
||||
// restart instead.
|
||||
//
|
||||
// This works for code too, not just values: Most code changes can be
|
||||
// tested with just a hot reload.
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
useMaterial3: true,
|
||||
),
|
||||
theme: isDarkMode ? ThemeData.dark() : ThemeData.light(),
|
||||
home: const MyHomePage(title: 'Flutter Demo Home Page'),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
const MyHomePage({super.key, required this.title});
|
||||
|
||||
// This widget is the home page of your application. It is stateful, meaning
|
||||
// that it has a State object (defined below) that contains fields that affect
|
||||
// how it looks.
|
||||
|
||||
// This class is the configuration for the state. It holds the values (in this
|
||||
// case the title) provided by the parent (in this case the App widget) and
|
||||
// used by the build method of the State. Fields in a Widget subclass are
|
||||
// always marked "final".
|
||||
|
||||
final String title;
|
||||
|
||||
static ValueNotifier<bool> isDarkMode = ValueNotifier<bool>(false);
|
||||
@override
|
||||
State<MyHomePage> createState() => _MyHomePageState();
|
||||
}
|
||||
@ -77,32 +54,18 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
|
||||
final _currentIndex = 0.obs;
|
||||
final List<Widget> _pages = [
|
||||
Container(
|
||||
color: Colors.green,
|
||||
),
|
||||
// PassTrack(
|
||||
// date: '',
|
||||
// ),
|
||||
Real(),
|
||||
RealView(),
|
||||
Container(
|
||||
color: Colors.blue,
|
||||
),
|
||||
Container(
|
||||
color: Colors.yellow,
|
||||
),
|
||||
Container(
|
||||
color: Colors.purple,
|
||||
),
|
||||
HistoryRecord(),
|
||||
SettingPortrait()
|
||||
];
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.of(context).size;
|
||||
// This method is rerun every time setState is called, for instance as done
|
||||
// by the _incrementCounter method above.
|
||||
//
|
||||
// The Flutter framework has been optimized to make rerunning build methods
|
||||
// fast, so that you can just rebuild anything that needs updating rather
|
||||
// than having to individually change instances of widgets.
|
||||
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
drawerEdgeDragWidth: 0.0, // 禁止通过滑动打开drawer
|
||||
@ -113,12 +76,6 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
)
|
||||
: null,
|
||||
appBar: PreferredSize(
|
||||
// TRY THIS: Try changing the color here to a specific color (to
|
||||
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
|
||||
// change color while the other colors stay the same.
|
||||
// backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||
// Here we take the value from the MyHomePage object that was created by
|
||||
// the App.build method, and use it to set our appbar title.
|
||||
preferredSize: Size.fromHeight(appBarHeight),
|
||||
child: CustomAppBar(appBarHeight: 56, currentIndex: _currentIndex),
|
||||
),
|
||||
|
@ -1,75 +0,0 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
import 'package:cpnav/models/user.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();
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import 'package:cpnav/pages/real_data/realController.dart';
|
||||
import 'package:cpnav/pages/real/realController.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:scence_map/controllers/controller.dart';
|
||||
|
271
lib/pages/history/component/canlender_select.dart
Normal file
@ -0,0 +1,271 @@
|
||||
import 'package:calendar_date_picker2/calendar_date_picker2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
dynamic textValue;
|
||||
dynamic constructionDataAll;
|
||||
|
||||
class CanlenderSelect extends StatefulWidget {
|
||||
final String title;
|
||||
final List constructionData;
|
||||
final double height;
|
||||
final void Function(String?) getSelectedValue;
|
||||
const CanlenderSelect(
|
||||
{super.key,
|
||||
required this.title,
|
||||
required this.constructionData,
|
||||
required this.getSelectedValue,
|
||||
required this.height});
|
||||
|
||||
@override
|
||||
State<CanlenderSelect> createState() => _CanlenderSelectState();
|
||||
}
|
||||
|
||||
class _CanlenderSelectState extends State<CanlenderSelect> {
|
||||
//初始选中
|
||||
List<DateTime?> _dialogCalendarPickerValue = [
|
||||
constructionDataAll == null ? DateTime.now() : constructionDataAll[0]
|
||||
];
|
||||
bool _initialized = false;
|
||||
//因为父页面加载的时候会执行tools这时候init就会执行,导致日期不会初始化选中,使用update配合一个flag,让第一次数据加载的时候,初始化选中
|
||||
@override
|
||||
void didUpdateWidget(CanlenderSelect oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (!_initialized &&
|
||||
oldWidget.constructionData != widget.constructionData) {
|
||||
setState(() {
|
||||
// print(widget.constructionData?.first);
|
||||
if (widget.constructionData.isNotEmpty) {
|
||||
_dialogCalendarPickerValue = [
|
||||
DateTime.parse(widget.constructionData.first)
|
||||
];
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
var dayTextStyle = TextStyle(
|
||||
color: isDarkMode ? Colors.white : Colors.black,
|
||||
fontWeight: FontWeight.w700);
|
||||
final weekendTextStyle =
|
||||
TextStyle(color: Colors.grey[500], fontWeight: FontWeight.w600);
|
||||
final config = CalendarDatePicker2WithActionButtonsConfig(
|
||||
// buttonPadding: EdgeInsets.only(left: 10,right: 10),
|
||||
// cancelButtonTextStyle: TextStyle(fontSize: 8,color: Colors.purple,fontWeight: FontWeight.w700),
|
||||
// okButtonTextStyle: TextStyle(fontSize: 8,color: Colors.purple,fontWeight: FontWeight.w700),
|
||||
// controlsHeight: 16,
|
||||
gapBetweenCalendarAndButtons: 0,
|
||||
dayTextStyle: dayTextStyle,
|
||||
calendarType: CalendarDatePicker2Type.single,
|
||||
lastDate: DateTime.now(),
|
||||
selectedDayHighlightColor: Colors.purple[800],
|
||||
closeDialogOnCancelTapped: true,
|
||||
firstDayOfWeek: 1,
|
||||
weekdayLabelTextStyle: TextStyle(
|
||||
color: isDarkMode ? Colors.white : Colors.black87,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
controlsTextStyle: TextStyle(
|
||||
color: isDarkMode ? Colors.white : Colors.black,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
centerAlignModePicker: true,
|
||||
customModePickerIcon: const SizedBox(),
|
||||
selectedDayTextStyle: dayTextStyle.copyWith(
|
||||
color: isDarkMode ? Colors.black : Colors.white),
|
||||
dayTextStylePredicate: ({required date}) {
|
||||
TextStyle? textStyle;
|
||||
if (date.weekday == DateTime.saturday ||
|
||||
date.weekday == DateTime.sunday) {
|
||||
textStyle = weekendTextStyle;
|
||||
}
|
||||
// if (DateUtils.isSameDay(date, DateTime(2021, 1, 25))) {
|
||||
// textStyle = anniversaryTextStyle;
|
||||
// }
|
||||
return textStyle;
|
||||
},
|
||||
dayBuilder: ({
|
||||
required date,
|
||||
textStyle,
|
||||
decoration,
|
||||
isSelected,
|
||||
isDisabled,
|
||||
isToday,
|
||||
}) {
|
||||
Widget? dayWidget;
|
||||
for (var i = 0; i < widget.constructionData.length; i++) {
|
||||
if (date.year ==
|
||||
int.parse(
|
||||
widget.constructionData[i].toString().split("-")[0]) &&
|
||||
date.month ==
|
||||
int.parse(
|
||||
widget.constructionData[i].toString().split("-")[1]) &&
|
||||
date.day ==
|
||||
int.parse(
|
||||
widget.constructionData[i].toString().split("-")[2])) {
|
||||
dayWidget = Container(
|
||||
decoration: decoration,
|
||||
child: Center(
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.center,
|
||||
children: [
|
||||
Text(
|
||||
MaterialLocalizations.of(context).formatDecimal(date.day),
|
||||
style: textStyle,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 27.5),
|
||||
child: Container(
|
||||
height: 5,
|
||||
width: 5,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Colors.green,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return dayWidget;
|
||||
},
|
||||
yearBuilder: ({
|
||||
required year,
|
||||
decoration,
|
||||
isCurrentYear,
|
||||
isDisabled,
|
||||
isSelected,
|
||||
textStyle,
|
||||
}) {
|
||||
return Center(
|
||||
child: Container(
|
||||
decoration: decoration,
|
||||
height: 36,
|
||||
width: 72,
|
||||
child: Center(
|
||||
child: Semantics(
|
||||
selected: isSelected,
|
||||
button: true,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
year.toString(),
|
||||
style: textStyle,
|
||||
),
|
||||
if (isWorkedYear(year))
|
||||
Container(
|
||||
padding: const EdgeInsets.all(5),
|
||||
margin: const EdgeInsets.only(left: 5),
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.green,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (widget.height > 410) {
|
||||
final values = await showCalendarDatePicker2Dialog(
|
||||
dialogBackgroundColor: isDarkMode ? Colors. black: Colors.white,
|
||||
|
||||
context: context,
|
||||
config: config,
|
||||
// dialogSize: const Size(300, 321),
|
||||
dialogSize: const Size(300, 410),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
value: _dialogCalendarPickerValue,
|
||||
);
|
||||
if (values != null) {
|
||||
// ignore: avoid_print
|
||||
// print(_getValueText(
|
||||
// config.calendarType,
|
||||
// values,
|
||||
// ));
|
||||
setState(() {
|
||||
_dialogCalendarPickerValue = values;
|
||||
textValue = values;
|
||||
});
|
||||
widget.getSelectedValue(values[0].toString().split(" ")[0]);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(textValue == null
|
||||
? (widget.constructionData.isEmpty)
|
||||
? ""
|
||||
: widget.constructionData[0]
|
||||
: textValue[0].toString().split(" ")[0]),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool isWorkedYear(year) {
|
||||
for (var i = 0; i < widget.constructionData.length; i++) {
|
||||
if (int.parse(widget.constructionData[i].toString().split("-")[0]) ==
|
||||
year) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String getValueText(
|
||||
CalendarDatePicker2Type datePickerType,
|
||||
List<DateTime?> values,
|
||||
) {
|
||||
values =
|
||||
values.map((e) => e != null ? DateUtils.dateOnly(e) : null).toList();
|
||||
var valueText = (values.isNotEmpty ? values[0] : null)
|
||||
.toString()
|
||||
.replaceAll('00:00:00.000', '');
|
||||
|
||||
if (datePickerType == CalendarDatePicker2Type.multi) {
|
||||
valueText = values.isNotEmpty
|
||||
? values
|
||||
.map((v) => v.toString().replaceAll('00:00:00.000', ''))
|
||||
.join(', ')
|
||||
: 'null';
|
||||
} else if (datePickerType == CalendarDatePicker2Type.range) {
|
||||
if (values.isNotEmpty) {
|
||||
final startDate = values[0].toString().replaceAll('00:00:00.000', '');
|
||||
final endDate = values.length > 1
|
||||
? values[1].toString().replaceAll('00:00:00.000', '')
|
||||
: 'null';
|
||||
valueText = '$startDate to $endDate';
|
||||
} else {
|
||||
return 'null';
|
||||
}
|
||||
}
|
||||
|
||||
return valueText;
|
||||
}
|
||||
|
||||
|
||||
}
|
121
lib/pages/history/component/custom_pager.dart
Normal file
@ -0,0 +1,121 @@
|
||||
import 'package:data_table_2/data_table_2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PageNumber extends StatefulWidget {
|
||||
const PageNumber({
|
||||
super.key,
|
||||
required PaginatorController controller,
|
||||
}) : _controller = controller;
|
||||
|
||||
final PaginatorController _controller;
|
||||
|
||||
@override
|
||||
PageNumberState createState() => PageNumberState();
|
||||
}
|
||||
|
||||
class PageNumberState extends State<PageNumber> {
|
||||
void update() {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
widget._controller.addListener(update);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget._controller.removeListener(update);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Checking instance id to see if build is called
|
||||
// on different ones
|
||||
// Due to some reasons when using this widget
|
||||
// with AsyncPaginatedDatatable2 the widget is instatiotaed once
|
||||
// though it's state is created 3 times upon first loading
|
||||
// of the Custom pager example
|
||||
// print(identityHashCode(this));
|
||||
return Text(widget._controller.isAttached
|
||||
? 'Page: ${1 + ((widget._controller.currentRowIndex + 1) / widget._controller.rowsPerPage).floor()} of '
|
||||
'${(widget._controller.rowCount / widget._controller.rowsPerPage).ceil()}'
|
||||
: 'Page: x of y');
|
||||
}
|
||||
}
|
||||
|
||||
class CustomPager extends StatefulWidget {
|
||||
const CustomPager(this.controller, {super.key});
|
||||
|
||||
final PaginatorController controller;
|
||||
|
||||
@override
|
||||
CustomPagerState createState() => CustomPagerState();
|
||||
}
|
||||
|
||||
class CustomPagerState extends State<CustomPager> {
|
||||
static const List<int> _availableSizes = [10, 20, 30, 50, 100];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
widget.controller.addListener(() {
|
||||
// setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
// skip this build pass
|
||||
if (!widget.controller.isAttached) return const SizedBox();
|
||||
return Container(
|
||||
height: 40,
|
||||
margin: const EdgeInsets.fromLTRB(5, 0, 0, 0),
|
||||
decoration: BoxDecoration(
|
||||
color: isDarkMode
|
||||
? Colors.black38
|
||||
: const Color.fromARGB(255, 230, 228, 228),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
child: Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
iconTheme: IconThemeData(
|
||||
color: isDarkMode ? Colors.white : Colors.black),
|
||||
textTheme: TextTheme(
|
||||
titleMedium: TextStyle(
|
||||
color: isDarkMode ? Colors.white : Colors.black))),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () => widget.controller.goToFirstPage(),
|
||||
icon: const Icon(Icons.skip_previous)),
|
||||
IconButton(
|
||||
onPressed: () => widget.controller.goToPreviousPage(),
|
||||
icon: const Icon(Icons.chevron_left_sharp)),
|
||||
DropdownButton<int>(
|
||||
onChanged: (v) => widget.controller.setRowsPerPage(v!),
|
||||
value: _availableSizes.contains(widget.controller.rowsPerPage)
|
||||
? widget.controller.rowsPerPage
|
||||
: _availableSizes[0],
|
||||
dropdownColor: Colors.grey,
|
||||
items: _availableSizes
|
||||
.map((s) => DropdownMenuItem<int>(
|
||||
value: s,
|
||||
child: Text(s.toString()),
|
||||
))
|
||||
.toList()),
|
||||
IconButton(
|
||||
onPressed: () => widget.controller.goToNextPage(),
|
||||
icon: const Icon(Icons.chevron_right_sharp)),
|
||||
IconButton(
|
||||
onPressed: () => widget.controller.goToLastPage(),
|
||||
icon: const Icon(Icons.skip_next))
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
253
lib/pages/history/history_record.dart
Normal file
@ -0,0 +1,253 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:data_table_2/data_table_2.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:scence_map/record_entity.dart';
|
||||
|
||||
import '../../service/base.dart';
|
||||
import 'component/canlender_select.dart';
|
||||
import 'component/custom_pager.dart';
|
||||
import 'record_data_source.dart';
|
||||
import 'tableCol.dart';
|
||||
|
||||
// import 'record_data_source.dart'; // bug 切换到对应proj_type 下
|
||||
|
||||
|
||||
int currentPage = 1;
|
||||
List dataList = [];
|
||||
List constructionData = [];
|
||||
String? selectedDataString;
|
||||
bool isfirst = true;
|
||||
|
||||
class HistoryRecord extends StatefulWidget {
|
||||
const HistoryRecord({super.key});
|
||||
|
||||
@override
|
||||
State<HistoryRecord> createState() => _HistoryRecordState();
|
||||
}
|
||||
|
||||
class _HistoryRecordState extends State<HistoryRecord> {
|
||||
void sort<T>(
|
||||
Comparable<T> Function(RecordEntity d) getField,
|
||||
int columnIndex,
|
||||
bool ascending,
|
||||
) {
|
||||
setState(() {
|
||||
_sortColumnIndex = columnIndex;
|
||||
_sortAscending = ascending;
|
||||
});
|
||||
}
|
||||
|
||||
PageSyncApproach pageSyncApproach = PageSyncApproach.doNothing;
|
||||
|
||||
List<TableCol> tableTitle = [
|
||||
// 不能设置宽度超过 一定的值 不然会报错 尽量不设置宽度 剩余更多的宽度来均分
|
||||
TableCol("pileId", "桩点", 60, null
|
||||
// (columnIndex, ascending) =>
|
||||
// sort<num>((d) => d.pileId, columnIndex, ascending)
|
||||
),
|
||||
TableCol('x', "坐标", 140, null),
|
||||
TableCol('startTime', "开始时间", 170, null),
|
||||
TableCol('endTime', "结束时间", 170, null),
|
||||
TableCol('current1Avg', "电流(A)", 0, null),
|
||||
TableCol('totalFlow', "累计浆量", 0, null),
|
||||
TableCol('depth', "深度", 0, null),
|
||||
TableCol('speed', "速度", 0, null),
|
||||
TableCol('tiltAngle', "倾斜角", 0, null),
|
||||
TableCol('operation', "操作", 0, null),
|
||||
TableCol('operation', "操作", 0, null)
|
||||
];
|
||||
List<DataColumn2> tableTitleWidget = [];
|
||||
TextStyle fontstyle = const TextStyle(fontSize: 20);
|
||||
TextStyle buttomfontstyle = const TextStyle(fontSize: 16);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
for (var i = 0; i < tableTitle.length; i++) {
|
||||
TableCol row = tableTitle[i];
|
||||
if (row.props == "pileId") {
|
||||
//模型转化后的字段
|
||||
row.onSort = (columnIndex, ascending) {
|
||||
sort<num>((d) => d.pileId, columnIndex, ascending);
|
||||
_asyncrecordsDataSource.sort(row.props, _sortAscending);
|
||||
};
|
||||
}
|
||||
if (row.width != 0) {
|
||||
tableTitleWidget.add(DataColumn2(
|
||||
label: Text(
|
||||
row.label,
|
||||
// style: fontstyle,
|
||||
),
|
||||
size: ColumnSize.S,
|
||||
fixedWidth: row.width,
|
||||
// numeric: true,
|
||||
onSort: row.onSort,
|
||||
));
|
||||
} else {
|
||||
tableTitleWidget.add(DataColumn2(
|
||||
label: Text(
|
||||
row.label,
|
||||
),
|
||||
size: ColumnSize.S,
|
||||
onSort: row.onSort,
|
||||
));
|
||||
}
|
||||
}
|
||||
fetchData();
|
||||
super.initState();
|
||||
if (GetServices().projType == "pile_cm") {
|
||||
_asyncrecordsDataSource = RecordDataSourceAsync(
|
||||
_rowsPerPage, selectedDataString ?? "", context);
|
||||
} else {
|
||||
_asyncrecordsDataSource = RecordDataSourceAsync(
|
||||
_rowsPerPage, selectedDataString ?? "", context);
|
||||
}
|
||||
}
|
||||
|
||||
void getDateValue(String? value) {
|
||||
setState(() {
|
||||
selectedDataString = value;
|
||||
fetchData();
|
||||
});
|
||||
_asyncrecordsDataSource.refreshDatasource();
|
||||
}
|
||||
|
||||
void fetchData() {
|
||||
dataList.clear();
|
||||
// 异步加载数据或从API获取数据
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) async {
|
||||
List getConstructionData = await GetServices().getworkDateData();
|
||||
if (isfirst) {
|
||||
setState(() {
|
||||
if (getConstructionData.isNotEmpty) {
|
||||
selectedDataString = getConstructionData[0];
|
||||
isfirst = false;
|
||||
} else {
|
||||
selectedDataString =
|
||||
DateFormat('yyyy-MM-dd').format(DateTime.now());
|
||||
}
|
||||
constructionData = getConstructionData;
|
||||
});
|
||||
}
|
||||
setState(() {
|
||||
if (getConstructionData.isEmpty) {
|
||||
constructionData = [selectedDataString];
|
||||
} else {
|
||||
constructionData = getConstructionData;
|
||||
}
|
||||
});
|
||||
_asyncrecordsDataSource.date = selectedDataString ?? "";
|
||||
|
||||
_asyncrecordsDataSource.refreshDatasource();
|
||||
});
|
||||
}
|
||||
|
||||
TableCell contentTableCell(child) {
|
||||
return TableCell(
|
||||
verticalAlignment: TableCellVerticalAlignment.middle,
|
||||
child: Center(
|
||||
child: child,
|
||||
));
|
||||
}
|
||||
|
||||
late RecordDataSourceAsync _asyncrecordsDataSource;
|
||||
bool _initialized = false;
|
||||
final ScrollController _controller = ScrollController();
|
||||
PaginatorController pageController = PaginatorController();
|
||||
int _rowsPerPage = 20;
|
||||
final int _fixedRows = 1;
|
||||
int? _sortColumnIndex = 0;
|
||||
bool _sortAscending = false;
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
if (!_initialized) {
|
||||
fetchData();
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.of(context).size;
|
||||
final isDarkMode =
|
||||
Theme.of(context).brightness == Brightness.dark;
|
||||
return Container(
|
||||
height: size.height,
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(children: [
|
||||
Flexible(
|
||||
fit: FlexFit.tight,
|
||||
child: getAsyncPaginatedDataTable(size, isDarkMode))
|
||||
])),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget getAsyncPaginatedDataTable(Size size, bool isDarkMode) {
|
||||
return AsyncPaginatedDataTable2(
|
||||
// 总小于 100 ,100不显示 (不能设置 autoRowsToHeight 为true 会导致分页尺寸不显示)
|
||||
availableRowsPerPage: const [10, 20, 30, 50, 100],
|
||||
header: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(mainAxisAlignment: MainAxisAlignment.start, children: [
|
||||
// Text("日期:", style: fontstyle),
|
||||
CanlenderSelect(
|
||||
title: "",
|
||||
constructionData: constructionData,
|
||||
getSelectedValue: getDateValue,
|
||||
height: size.height,
|
||||
),
|
||||
CustomPager(pageController)
|
||||
]),
|
||||
),
|
||||
// autoRowsToHeight: true,
|
||||
hidePaginator: true,
|
||||
rowsPerPage: _rowsPerPage,
|
||||
showCheckboxColumn: false,
|
||||
scrollController: _controller,
|
||||
|
||||
controller: pageController,
|
||||
columnSpacing: 15,
|
||||
horizontalMargin: 10,
|
||||
border: const TableBorder(
|
||||
bottom: BorderSide(width: 0.5, color: Colors.grey),
|
||||
verticalInside: BorderSide(width: 0.5, color: Colors.grey),
|
||||
),
|
||||
headingRowColor: WidgetStateProperty.resolveWith(
|
||||
(states) => _fixedRows > 0 ? Colors.grey[200] : Colors.transparent),
|
||||
fixedColumnsColor: Colors.grey[300],
|
||||
fixedCornerColor: Colors.grey[400],
|
||||
minWidth: 1000,
|
||||
fixedTopRows: 1,
|
||||
fixedLeftColumns: 1,
|
||||
sortColumnIndex: _sortColumnIndex,
|
||||
sortAscending: _sortAscending,
|
||||
onRowsPerPageChanged: (int? value) {
|
||||
_rowsPerPage = value!;
|
||||
_asyncrecordsDataSource.refreshDatasource();
|
||||
},
|
||||
onPageChanged: (int value) {
|
||||
_asyncrecordsDataSource.refreshDatasource();
|
||||
},
|
||||
columns: tableTitleWidget,
|
||||
source: _asyncrecordsDataSource,
|
||||
empty: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
color: Colors.grey[200],
|
||||
child: const Text('暂无数据'))),
|
||||
);
|
||||
}
|
||||
}
|
156
lib/pages/history/record_data_source.dart
Normal file
@ -0,0 +1,156 @@
|
||||
import 'package:data_table_2/data_table_2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:scence_map/record_entity.dart';
|
||||
import 'package:scence_map/scence_map.dart';
|
||||
|
||||
import '../../service/base.dart';
|
||||
|
||||
class RecordDataSourceAsync extends AsyncDataTableSource {
|
||||
bool _empty = false;
|
||||
int size;
|
||||
int page = 1;
|
||||
String date;
|
||||
BuildContext context;
|
||||
final RecordFakeWebService _repo = RecordFakeWebService();
|
||||
RecordDataSourceAsync(this.size, this.date, this.context) {
|
||||
// print(' created');
|
||||
}
|
||||
RecordDataSourceAsync.empty(this.size, this.date, this.context) {
|
||||
_empty = true;
|
||||
// print('empty created');
|
||||
}
|
||||
|
||||
String _sortColumn = "tp_id";
|
||||
String _sortAscending = "desc";
|
||||
|
||||
void sort(String columnName, bool ascending) {
|
||||
_sortColumn = columnName == "tpId" ? "tp_id" : columnName;
|
||||
_sortAscending = ascending ? "asc" : "desc";
|
||||
refreshDatasource();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<AsyncRowsResponse> getRows(int startIndex, int count) async {
|
||||
// print('getRows($startIndex, $count)');
|
||||
page = startIndex ~/ count + 1;
|
||||
|
||||
var data = await GetServices()
|
||||
.getRcordData(page, count, date, _sortAscending, _sortColumn);
|
||||
if (data == null) {
|
||||
_empty = true;
|
||||
}
|
||||
// if (_errorCounter != null) {
|
||||
// _errorCounter = _errorCounter! + 1;
|
||||
|
||||
// if (_errorCounter! % 2 == 1) {
|
||||
// await Future.delayed(const Duration(milliseconds: 1000));
|
||||
// throw 'Error #${((_errorCounter! - 1) / 2).round() + 1} has occured';
|
||||
// }
|
||||
// }
|
||||
|
||||
// final format = NumberFormat.decimalPercentPattern(
|
||||
// locale: 'en',
|
||||
// decimalDigits: 0,
|
||||
// );
|
||||
// assert(startIndex >= 0);
|
||||
|
||||
// List returned will be empty is there're fewer items than startingAt
|
||||
var x = _empty
|
||||
? await Future.delayed(const Duration(milliseconds: 0),
|
||||
() => RecordFakeWebServiceResponse(0, []))
|
||||
: await _repo.getData(startIndex, count, data);
|
||||
|
||||
AsyncRowsResponse r = AsyncRowsResponse(
|
||||
x.totalRecords,
|
||||
x.data.map((item) {
|
||||
return DataRow(
|
||||
key: ValueKey<int>(item.id),
|
||||
selected: item.selected,
|
||||
onSelectChanged: (value) {
|
||||
if (value != null) {
|
||||
// setRowSelection(ValueKey<int>(RecordEntity.id), value);
|
||||
}
|
||||
},
|
||||
cells: [
|
||||
// DataCell(Text(item.tpId.toString())),
|
||||
DataCell(Text(
|
||||
'x:${item.x.toStringAsFixed(3)}\ny:${item.y.toStringAsFixed(3)}')),
|
||||
DataCell(Text(
|
||||
DateFormat('yyyy-MM-dd HH:mm:ss').format(item.startTime))),
|
||||
DataCell(
|
||||
Text(DateFormat('yyyy-MM-dd HH:mm:ss').format(item.endTime))),
|
||||
DataCell(TextButton(
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const Scaffold(
|
||||
body: ScenceMapView(
|
||||
|
||||
children: [],
|
||||
// DateFormat('yyyy-MM-dd')
|
||||
// .format(item.startTime)
|
||||
),
|
||||
))),
|
||||
child: const Text("位置"),
|
||||
)),
|
||||
// DataCell(TextButton(
|
||||
// onPressed: () => Navigator.push(
|
||||
// context,
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) => MyLineChart(
|
||||
// pileId: item.tpId,
|
||||
// ))),
|
||||
// child: const Text("过程"),
|
||||
// ))
|
||||
],
|
||||
);
|
||||
}).toList());
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
class RecordFakeWebServiceResponse {
|
||||
RecordFakeWebServiceResponse(this.totalRecords, this.data);
|
||||
|
||||
/// THe total ammount of records on the server, e.g. 100
|
||||
final int totalRecords;
|
||||
|
||||
/// One page, e.g. 10 reocrds
|
||||
final List<RecordEntity> data;
|
||||
}
|
||||
|
||||
class RecordFakeWebService {
|
||||
// int Function(RecordEntity, RecordEntity)? _getComparisonFunction(
|
||||
// String column, bool ascending) {
|
||||
// var coef = ascending ? 1 : -1;
|
||||
// switch (column) {
|
||||
// case 'pileId':
|
||||
// return (RecordEntity d1, RecordEntity d2) =>
|
||||
// coef * d1.pileId.compareTo(d2.pileId);
|
||||
// case 'name':
|
||||
// return (RecordEntity d1, RecordEntity d2) =>
|
||||
// d1.name.compareTo(d2.name) * coef;
|
||||
// }
|
||||
|
||||
// return null;
|
||||
// }
|
||||
|
||||
Future<RecordFakeWebServiceResponse> getData(
|
||||
int startingAt, int count, Map? data) async {
|
||||
List<RecordEntity> result = [];
|
||||
|
||||
for (var i = 0; i < (data!["list"] as List).length; i++) {
|
||||
RecordEntity item = RecordEntity.fromJson(data["list"][i]);
|
||||
result.add(item);
|
||||
}
|
||||
|
||||
return Future.delayed(const Duration(milliseconds: 0), () {
|
||||
// result.sort(_getComparisonFunction(sortedBy, sortedAsc));
|
||||
int total = data["pagination"]["total"];
|
||||
return RecordFakeWebServiceResponse(
|
||||
total , result.toList());
|
||||
});
|
||||
}
|
||||
}
|
12
lib/pages/history/tableCol.dart
Normal file
@ -0,0 +1,12 @@
|
||||
class TableCol {
|
||||
String props;
|
||||
String label;
|
||||
double width;
|
||||
Function(int, bool)? onSort;
|
||||
TableCol(
|
||||
this.props,
|
||||
this.label,
|
||||
this.width,
|
||||
this.onSort,
|
||||
);
|
||||
}
|
@ -3,9 +3,10 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:cpnav/pages/login/loginprefs.dart';
|
||||
import 'package:cpnav/service/base.dart';
|
||||
|
||||
import '../../service/user/loginprefs.dart';
|
||||
|
||||
class Login extends StatefulWidget {
|
||||
const Login({super.key});
|
||||
|
||||
|
@ -1,75 +0,0 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
import 'package:cpnav/models/user.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();
|
||||
}
|
||||
}
|
363
lib/pages/real/component/chart.dart
Normal file
@ -0,0 +1,363 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../service/base.dart';
|
||||
import '../process.dart';
|
||||
|
||||
|
||||
class ProcessChart extends StatefulWidget {
|
||||
final int pileId;
|
||||
const ProcessChart({super.key, required this.pileId});
|
||||
|
||||
@override
|
||||
State<ProcessChart> createState() => _ProcessChartState();
|
||||
}
|
||||
|
||||
class _ProcessChartState extends State<ProcessChart> {
|
||||
List<ProcessEntity> processList = [];
|
||||
List<Widget> chartTitleWidget = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
fetchData();
|
||||
}
|
||||
|
||||
int maxX = 5;
|
||||
int maxYL = 14; //深度
|
||||
int maxYR = 0; //10cm
|
||||
ChartData chartData = ChartData([]);
|
||||
void fetchData() async {
|
||||
try {
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) async {
|
||||
//获取点的数据
|
||||
List detailCdate = await GetServices().getProcessData(widget.pileId);
|
||||
|
||||
setState(() {
|
||||
double ten1Max = 0;
|
||||
double ten2Max = 0;
|
||||
for (var i = 0; i < detailCdate.length; i++) {
|
||||
ProcessEntity process = ProcessEntity.fromJson(detailCdate[i]);
|
||||
processList.add(process);
|
||||
// 左(深度)右(10cm流量)标题栏最大值计算
|
||||
maxYL = max(maxYL, process.depth.ceil());
|
||||
ten1Max = max(ten1Max, process.subtotalFlow1);
|
||||
ten2Max = max(ten2Max, process.subtotalFlow2);
|
||||
maxYR = max(ten2Max.ceil(), ten1Max.ceil());
|
||||
}
|
||||
int radtio = (maxYR / maxYL).ceil();
|
||||
chartData = ChartData(processList, maxX, maxYL, maxYR, radtio);
|
||||
chartTitleWidget = chartData.chartTitleWeidget();
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
print('错误:$e');
|
||||
setState(() {
|
||||
processList = [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
SystemChrome.setPreferredOrientations([]);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: chartTitleWidget)),
|
||||
LineChart(chartData.lineChart)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChartTileModel {
|
||||
String title;
|
||||
Color colors;
|
||||
ChartTileModel(
|
||||
this.title,
|
||||
this.colors,
|
||||
);
|
||||
}
|
||||
|
||||
class ChartData {
|
||||
List<ProcessEntity> processList;
|
||||
int maxX;
|
||||
// 这3个值变化
|
||||
int maxYL;
|
||||
int maxYR;
|
||||
int radtio;
|
||||
List<ChartTileModel> chartTitle = [
|
||||
ChartTileModel("深度:", Colors.green),
|
||||
ChartTileModel("10cm流量1:", Colors.pink),
|
||||
ChartTileModel("10cm流量2:", Colors.cyan),
|
||||
];
|
||||
ChartData(this.processList,
|
||||
[this.maxX = 5, this.maxYL = 14, this.maxYR = 100, this.radtio = 8]);
|
||||
|
||||
List<Widget> chartTitleWeidget() {
|
||||
List<Widget> chartTitleWidget = [];
|
||||
for (var i = 0; i < chartTitle.length; i++) {
|
||||
ChartTileModel tileModel = chartTitle[i];
|
||||
List<Widget> item = [
|
||||
Text(tileModel.title),
|
||||
Container(
|
||||
width: 13,
|
||||
height: 8,
|
||||
margin: const EdgeInsets.only(right: 5),
|
||||
decoration: BoxDecoration(color: tileModel.colors),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
height: 10,
|
||||
),
|
||||
];
|
||||
chartTitleWidget.addAll(item);
|
||||
}
|
||||
return chartTitleWidget;
|
||||
}
|
||||
|
||||
LineChartData get lineChart => LineChartData(
|
||||
lineTouchData: lineTouchData,
|
||||
gridData: gridData,
|
||||
titlesData: titlesData,
|
||||
borderData: borderData,
|
||||
lineBarsData: lineBarsData,
|
||||
minX: 0,
|
||||
maxX: maxX.toDouble(),
|
||||
maxY: maxYR.toDouble(),
|
||||
minY: 0,
|
||||
);
|
||||
|
||||
LineTouchData get lineTouchData => LineTouchData(
|
||||
handleBuiltInTouches: true,
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
fitInsideHorizontally: true,
|
||||
fitInsideVertically: true,
|
||||
// tooltipBgColor: Colors.black38.withOpacity(0.8),
|
||||
getTooltipItems: (List<LineBarSpot> touchedSpots) {
|
||||
// 自定义提示框内容
|
||||
return touchedSpots.map((LineBarSpot touchedSpot) {
|
||||
// 找到对应的数据点的索引
|
||||
final x = touchedSpot.x;
|
||||
int index = (x / maxX * processList.length).round();
|
||||
ProcessEntity process = processList[index];
|
||||
String lineToolValue = "";
|
||||
TextStyle lineToolStyle = const TextStyle(
|
||||
color: Colors.green,
|
||||
);
|
||||
|
||||
if (touchedSpot.barIndex == 0) {
|
||||
lineToolValue =
|
||||
"时间:${DateFormat('HH:mm:ss').format(DateTime.parse(process.recvTime)).toString()}\n深度:${process.depth}\n累计流量:${process.toatalFlow1}";
|
||||
lineToolStyle = const TextStyle(color: Colors.green);
|
||||
} else if (touchedSpot.barIndex == 1) {
|
||||
lineToolValue = "10cm流量1:${process.subtotalFlow1}";
|
||||
lineToolStyle = const TextStyle(color: Colors.pink);
|
||||
} else if (touchedSpot.barIndex == 2) {
|
||||
lineToolValue = "10cm流量2:${process.subtotalFlow2}";
|
||||
lineToolStyle = const TextStyle(color: Colors.cyan);
|
||||
}
|
||||
|
||||
return LineTooltipItem(lineToolValue, lineToolStyle,
|
||||
textAlign: TextAlign.left);
|
||||
}).toList();
|
||||
},
|
||||
),
|
||||
);
|
||||
// FlGridData get gridData => const FlGridData(show: false);
|
||||
//网格
|
||||
FlGridData get gridData => const FlGridData(drawHorizontalLine: true);
|
||||
//设置标题
|
||||
FlTitlesData get titlesData => FlTitlesData(
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: bottomTitles,
|
||||
),
|
||||
rightTitles: AxisTitles(
|
||||
sideTitles: rightTitles(),
|
||||
),
|
||||
topTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: leftTitles(),
|
||||
),
|
||||
);
|
||||
|
||||
SideTitles get bottomTitles => SideTitles(
|
||||
showTitles: true,
|
||||
reservedSize: 32,
|
||||
interval: 1,
|
||||
getTitlesWidget: bottomTitleWidgets,
|
||||
);
|
||||
SideTitles leftTitles() => SideTitles(
|
||||
getTitlesWidget: leftTitleWidgets,
|
||||
showTitles: true,
|
||||
interval: 1,
|
||||
reservedSize: 40,
|
||||
);
|
||||
SideTitles rightTitles() => SideTitles(
|
||||
getTitlesWidget: rightTitleWidgets,
|
||||
showTitles: true,
|
||||
interval: 1,
|
||||
reservedSize: 40,
|
||||
);
|
||||
|
||||
//x轴
|
||||
Widget bottomTitleWidgets(double value, TitleMeta meta) {
|
||||
const style = TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
);
|
||||
|
||||
String getTimeFromIndex(int index) {
|
||||
if (processList.isEmpty || index < 0 || index >= processList.length) {
|
||||
return '';
|
||||
}
|
||||
return processList[index].recvTime.toString().split(" ")[1];
|
||||
}
|
||||
|
||||
int index;
|
||||
|
||||
switch (value.toInt()) {
|
||||
case 0:
|
||||
index = 0;
|
||||
break;
|
||||
case 1:
|
||||
index = (processList.length / 5).round();
|
||||
break;
|
||||
case 2:
|
||||
index = (processList.length * 2 / 5).round();
|
||||
break;
|
||||
case 3:
|
||||
index = (processList.length * 3 / 5).round();
|
||||
break;
|
||||
case 4:
|
||||
index = (processList.length * 4 / 5).round();
|
||||
break;
|
||||
case 5:
|
||||
index = processList.length - 1;
|
||||
break;
|
||||
default:
|
||||
return SideTitleWidget(
|
||||
axisSide: meta.axisSide,
|
||||
space: 10,
|
||||
child: const Text(''),
|
||||
);
|
||||
}
|
||||
|
||||
return SideTitleWidget(
|
||||
axisSide: meta.axisSide,
|
||||
space: 10,
|
||||
child: Text(
|
||||
getTimeFromIndex(index),
|
||||
style: style,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final style = const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
);
|
||||
//左y轴
|
||||
Widget leftTitleWidgets(double value, TitleMeta meta) {
|
||||
String text;
|
||||
int intValue = (value).ceil();
|
||||
if (intValue % (2 * radtio) == 0 && intValue <= maxYR) {
|
||||
text = (intValue / radtio).floor().toString();
|
||||
if (maxYR - intValue < 2 * radtio) {
|
||||
text = "深度(m)";
|
||||
}
|
||||
} else {
|
||||
return const Text("");
|
||||
}
|
||||
|
||||
return Text(text, style: style, textAlign: TextAlign.center);
|
||||
}
|
||||
|
||||
//右y轴
|
||||
Widget rightTitleWidgets(double value, TitleMeta meta) {
|
||||
String text;
|
||||
int intValue = value.toInt();
|
||||
|
||||
if (intValue >= 0 && intValue % (2 * radtio) == 0 && intValue <= maxYR) {
|
||||
text = (intValue.ceil()).toString();
|
||||
if (maxYR - intValue < 2 * radtio) {
|
||||
text = "流量(L)";
|
||||
}
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return Text(text, style: style, textAlign: TextAlign.center);
|
||||
}
|
||||
|
||||
//边界数据
|
||||
FlBorderData get borderData => FlBorderData(
|
||||
show: true,
|
||||
border: const Border(
|
||||
bottom: BorderSide(color: Colors.grey, width: 4),
|
||||
left: BorderSide(color: Colors.transparent),
|
||||
right: BorderSide(color: Colors.transparent),
|
||||
top: BorderSide(color: Colors.transparent),
|
||||
),
|
||||
);
|
||||
//数据
|
||||
List<LineChartBarData> get lineBarsData => [
|
||||
lineChartBarDataTen1,
|
||||
lineChartBarDataTen2,
|
||||
lineChartBarDataDepth,
|
||||
];
|
||||
LineChartBarData get lineChartBarDataDepth => LineChartBarData(
|
||||
isCurved: false,
|
||||
color: Colors.green,
|
||||
barWidth: 3,
|
||||
isStrokeCapRound: true,
|
||||
dotData: const FlDotData(show: false),
|
||||
belowBarData: BarAreaData(show: false),
|
||||
spots: processList.map((item) {
|
||||
int index = processList.indexOf(item);
|
||||
return FlSpot(
|
||||
(maxX / processList.length) * index, (item.depth * radtio));
|
||||
}).toList());
|
||||
|
||||
LineChartBarData get lineChartBarDataTen1 => LineChartBarData(
|
||||
isCurved: false,
|
||||
color: Colors.pink,
|
||||
barWidth: 3,
|
||||
isStrokeCapRound: true,
|
||||
dotData: const FlDotData(show: false),
|
||||
belowBarData: BarAreaData(
|
||||
show: false,
|
||||
color: Colors.pink,
|
||||
),
|
||||
spots: processList.map((item) {
|
||||
int index = processList.indexOf(item);
|
||||
return FlSpot((maxX / processList.length) * index, item.subtotalFlow1);
|
||||
}).toList());
|
||||
|
||||
LineChartBarData get lineChartBarDataTen2 => LineChartBarData(
|
||||
isCurved: false,
|
||||
color: Colors.cyan,
|
||||
barWidth: 3,
|
||||
isStrokeCapRound: true,
|
||||
dotData: const FlDotData(show: false),
|
||||
belowBarData: BarAreaData(show: false),
|
||||
spots: processList.map((item) {
|
||||
int index = processList.indexOf(item);
|
||||
return FlSpot((maxX / processList.length) * index, item.subtotalFlow2);
|
||||
}).toList());
|
||||
}
|
65
lib/pages/real/index.dart
Normal file
@ -0,0 +1,65 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'line_chart.dart';
|
||||
import 'realController.dart';
|
||||
import 'real_data.dart';
|
||||
|
||||
|
||||
final RealController realController = Get.find();
|
||||
|
||||
class Real extends StatelessWidget {
|
||||
const Real({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.of(context).size;
|
||||
// 设备获取
|
||||
return OrientationBuilder(builder: (context, orientation) {
|
||||
bool isPortrait = Orientation.portrait == orientation ? true : false;
|
||||
Widget space = const SizedBox(
|
||||
height: 5,
|
||||
width: 5,
|
||||
);
|
||||
List<Widget> children = [
|
||||
RealData(),
|
||||
space,
|
||||
const Divider(
|
||||
height: 1.0, // 线条的高度
|
||||
color: Colors.black, // 线条的颜色
|
||||
),
|
||||
space,
|
||||
// lineChart
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(child: RealChart()),
|
||||
Obx(() => Slider(
|
||||
value: realController.startIndex.toDouble(),
|
||||
onChanged: (newvalue) =>
|
||||
realController.updateSlider(newvalue),
|
||||
min: 0,
|
||||
max: realController.processList.length.toDouble(),
|
||||
)),
|
||||
],
|
||||
),
|
||||
)
|
||||
];
|
||||
if (isPortrait) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(2),
|
||||
height: size.height - 4,
|
||||
child: Column(children: children));
|
||||
} else {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(2),
|
||||
width: size.width - 4,
|
||||
child: Row(
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
113
lib/pages/real/line_chart.dart
Normal file
@ -0,0 +1,113 @@
|
||||
// ignore_for_file: must_be_immutable
|
||||
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'component/chart.dart';
|
||||
import 'process.dart';
|
||||
import 'realController.dart';
|
||||
|
||||
|
||||
|
||||
// 深度
|
||||
class RealChart extends StatelessWidget {
|
||||
RealChart({super.key});
|
||||
ChartData chartData = ChartData(
|
||||
[],
|
||||
);
|
||||
final RealController realController = Get.put(RealController());
|
||||
List<ProcessEntity> processList = [];
|
||||
format(double value, [int fix = 2]) {
|
||||
return double.parse(value.toStringAsFixed(fix));
|
||||
}
|
||||
|
||||
List<ProcessEntity> cache = [];
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 显示长度
|
||||
int showlength = 30;
|
||||
ever(realController.depth, (newValue) {
|
||||
ProcessEntity process = ProcessEntity(
|
||||
recvTime: DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now()),
|
||||
pileId: realController.pileId.value,
|
||||
utc: 0,
|
||||
tid: 100,
|
||||
toatalFlow1: format(realController.totalFlow1.value),
|
||||
toatalFlow2: format(realController.totalFlow2.value),
|
||||
subtotalFlow1: format(realController.subtotalFlow1.value),
|
||||
subtotalFlow2: format(realController.subtotalFlow2.value),
|
||||
depth: format(realController.depth.value),
|
||||
);
|
||||
|
||||
processList.add(process);
|
||||
|
||||
if (realController.sliderTime != null) {
|
||||
if (DateTime.now().difference(realController.sliderTime!).inSeconds >
|
||||
10) {
|
||||
realController.updateProcessList(processList);
|
||||
realController.updateSlider(processList.length.toDouble(), false);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
realController.updateProcessList(processList);
|
||||
if (realController.startIndex.value != processList.length) {
|
||||
realController.updateSlider(processList.length.toDouble(), false);
|
||||
}
|
||||
}
|
||||
});
|
||||
ever(realController.pileId, (newValue) {
|
||||
ProcessEntity process = ProcessEntity(
|
||||
recvTime: DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now()),
|
||||
pileId: realController.pileId.value,
|
||||
utc: 0,
|
||||
tid: 100,
|
||||
toatalFlow1: format(realController.totalFlow1.value),
|
||||
toatalFlow2: format(realController.totalFlow2.value),
|
||||
subtotalFlow1: format(realController.subtotalFlow1.value),
|
||||
subtotalFlow2: format(realController.subtotalFlow2.value),
|
||||
depth: format(realController.depth.value),
|
||||
);
|
||||
processList.length = 0;
|
||||
processList.add(process);
|
||||
realController.updateProcessList(processList.toList());
|
||||
realController.startIndex.value = 0;
|
||||
});
|
||||
|
||||
List<Widget> chartTitleWidget = chartData.chartTitleWeidget();
|
||||
Widget chartWidget(List<ProcessEntity> processList) {
|
||||
int startIndex = 0;
|
||||
|
||||
if ((realController.startIndex.value + showlength) < processList.length &&
|
||||
realController.startIndex.value != 0) {
|
||||
startIndex = realController.startIndex.value;
|
||||
} else {
|
||||
startIndex = ((processList.length - showlength < 0)
|
||||
? 0
|
||||
: (processList.length - showlength));
|
||||
}
|
||||
int endIndex = startIndex + showlength > processList.length
|
||||
? processList.length
|
||||
: startIndex + showlength;
|
||||
ChartData chartData = ChartData(
|
||||
processList.sublist(startIndex, endIndex),
|
||||
);
|
||||
return LineChart(chartData.lineChart);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: chartTitleWidget,
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Obx(
|
||||
() => SizedBox(child: chartWidget(realController.processList))))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
156
lib/pages/real/real_data.dart
Normal file
@ -0,0 +1,156 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../service/pile/device_type.dart';
|
||||
import 'realController.dart';
|
||||
|
||||
class RealItem extends StatelessWidget {
|
||||
final int flex;
|
||||
final List<Widget> children;
|
||||
const RealItem({super.key, required this.children, this.flex = 1});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(flex: flex, child: Row(children: children));
|
||||
}
|
||||
}
|
||||
|
||||
class RealData extends StatelessWidget {
|
||||
final RealController realController = Get.put(RealController());
|
||||
RealData({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 设备获取
|
||||
late TextStyle titleFontstyle;
|
||||
late TextStyle valueFontstyle;
|
||||
final deviceType = getDeviceType(context);
|
||||
if (deviceType == DeviceType.mobile) {
|
||||
titleFontstyle = const TextStyle(fontSize: 15);
|
||||
valueFontstyle = const TextStyle(fontSize: 36);
|
||||
} else {
|
||||
titleFontstyle = const TextStyle(fontSize: 22);
|
||||
valueFontstyle = const TextStyle(fontSize: 45);
|
||||
}
|
||||
Widget space = const SizedBox(
|
||||
height: 5,
|
||||
width: 5,
|
||||
);
|
||||
return Expanded(
|
||||
flex: 1,
|
||||
child: Column(
|
||||
children: [
|
||||
RealItem(children: [
|
||||
RealItemChild(
|
||||
// flex: 2,
|
||||
title: "速度(m/min)",
|
||||
titleFontstyle: titleFontstyle,
|
||||
child: Obx(() {
|
||||
return Text(
|
||||
realController.speed.abs() >= 100
|
||||
? realController.speed.toStringAsFixed(1)
|
||||
: realController.speed.toStringAsFixed(2),
|
||||
style: valueFontstyle,
|
||||
);
|
||||
}),
|
||||
),
|
||||
space,
|
||||
RealItemChild(
|
||||
// flex: 3,
|
||||
title: "时间(min:s)",
|
||||
titleFontstyle: titleFontstyle,
|
||||
child: Obx(() => Text(
|
||||
realController.time.value,
|
||||
style: valueFontstyle,
|
||||
))),
|
||||
space,
|
||||
RealItemChild(
|
||||
// flex: 2,
|
||||
title: "深度(m)",
|
||||
titleFontstyle: titleFontstyle,
|
||||
child: Obx(() => Text(
|
||||
realController.depth.toStringAsFixed(2),
|
||||
style: valueFontstyle,
|
||||
))),
|
||||
]),
|
||||
space,
|
||||
RealItem(children: [
|
||||
RealItemChild(
|
||||
// flex: 1,
|
||||
title: "1#瞬时流量(L/min)",
|
||||
titleFontstyle: titleFontstyle,
|
||||
child: Obx(() => Text(
|
||||
realController.subtotalFlow1.toStringAsFixed(2),
|
||||
style: valueFontstyle,
|
||||
))),
|
||||
space,
|
||||
RealItemChild(
|
||||
// flex: 1,
|
||||
title: "2#瞬时流量(L/min)",
|
||||
titleFontstyle: titleFontstyle,
|
||||
child: Obx(() => Text(
|
||||
realController.subtotalFlow2.toStringAsFixed(2),
|
||||
style: valueFontstyle,
|
||||
))),
|
||||
]),
|
||||
space,
|
||||
RealItem(children: [
|
||||
RealItemChild(
|
||||
// flex: 1,
|
||||
title: "1#累计流量(L)",
|
||||
titleFontstyle: titleFontstyle,
|
||||
child: Obx(() => Text(
|
||||
realController.totalFlow1.toStringAsFixed(2),
|
||||
style: valueFontstyle,
|
||||
))),
|
||||
space,
|
||||
RealItemChild(
|
||||
// flex: 1,
|
||||
title: "2#累计流量(L)",
|
||||
titleFontstyle: titleFontstyle,
|
||||
child: Obx(() => Text(
|
||||
realController.totalFlow2.toStringAsFixed(2),
|
||||
style: valueFontstyle,
|
||||
))),
|
||||
])
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class RealItemChild extends StatelessWidget {
|
||||
// final int flex;
|
||||
final String title;
|
||||
final Widget child;
|
||||
final TextStyle titleFontstyle;
|
||||
RealItemChild(
|
||||
{super.key,
|
||||
// required this.flex,
|
||||
required this.title,
|
||||
required this.child,
|
||||
required this.titleFontstyle});
|
||||
final BoxDecoration boxStyle = BoxDecoration(
|
||||
border: Border.all(color: Colors.black26),
|
||||
borderRadius: BorderRadius.circular(8.0));
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(
|
||||
// flex: flex,
|
||||
child: Container(
|
||||
decoration: boxStyle,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: titleFontstyle,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
child
|
||||
]),
|
||||
));
|
||||
}
|
||||
}
|
185
lib/pages/setting/child_pages/XyChange/connect.dart
Normal file
@ -0,0 +1,185 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../connect/bluetooth_page.dart';
|
||||
import '../connect/config/SocketSetting.dart';
|
||||
import '../connect/config/app_config.dart';
|
||||
import '../connect/config/blueParams.dart';
|
||||
import '../connect/config/connect_type.dart';
|
||||
import 'coor_trans.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);
|
||||
});
|
||||
}
|
||||
}
|
63
lib/pages/setting/child_pages/XyChange/coor_trans.dart
Normal 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) {}
|
||||
}
|
252
lib/pages/setting/child_pages/XyChange/xy_change.dart
Normal file
@ -0,0 +1,252 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../../service/base.dart';
|
||||
import '../../../../service/pile/device_type.dart';
|
||||
import '../connect/config/blueParams.dart';
|
||||
import '../connect/controllers/blueToothController.dart';
|
||||
import 'connect.dart';
|
||||
import 'coor_trans.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("更新设置"))
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
344
lib/pages/setting/child_pages/connect/bluetooth_page.dart
Normal file
@ -0,0 +1,344 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:quick_blue/quick_blue.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:open_settings/open_settings.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
import '../../../real/realController.dart';
|
||||
import 'config/app_config.dart';
|
||||
import 'config/blueParams.dart';
|
||||
import 'controllers/blueToothController.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),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import '../../../../real/index.dart';
|
||||
import '../bluetooth_page.dart';
|
||||
|
||||
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) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
69
lib/pages/setting/child_pages/connect/config/app_config.dart
Normal 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"],
|
||||
);
|
||||
}
|
||||
}
|
59
lib/pages/setting/child_pages/connect/config/blueParams.dart
Normal 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,
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
|
||||
enum ConnectType {
|
||||
wifi,
|
||||
bluetooth,
|
||||
none
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../config/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;
|
||||
}
|
252
lib/pages/setting/child_pages/connect/wifi_page.dart
Normal 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 'config/SocketSetting.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)));
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
import '../../../../models/user.dart';
|
||||
|
||||
import '../../../../service/base.dart';
|
||||
import '../../../../service/user/loginprefs.dart';
|
||||
import '../../component/list_view_item.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(), // 在每个列表项下方添加一条线
|
||||
],
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
26
lib/pages/setting/component/list_view_item.dart
Normal file
@ -0,0 +1,26 @@
|
||||
import 'package:flutter/material.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);
|
||||
}
|
139
lib/pages/setting/setting_page.dart
Normal file
@ -0,0 +1,139 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'child_pages/XyChange/xy_change.dart';
|
||||
import 'child_pages/connect/bluetooth_page.dart';
|
||||
import 'child_pages/persondetail/person_details.dart';
|
||||
import 'child_pages/connect/wifi_page.dart';
|
||||
|
||||
import 'component/list_view_item.dart';
|
||||
import 'child_pages/connect/config/app_config.dart';
|
||||
import 'child_pages/connect/config/connect_type.dart';
|
||||
import 'child_pages/connect/controllers/blueToothController.dart';
|
||||
|
||||
|
||||
//字体
|
||||
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(), // 在每个列表项下方添加一条线
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -1,8 +1,12 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
import 'package:cpnav/models/pilePoint/coord_trans.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'loginprefs.dart';
|
||||
|
||||
import 'user/loginprefs.dart';
|
||||
|
||||
|
||||
LoginPrefs loginPrefs = LoginPrefs();
|
||||
|
||||
@ -58,8 +62,6 @@ class BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
class PileCmController extends GetxController {}
|
||||
|
||||
class GetServices {
|
||||
BaseService service = BaseService();
|
||||
// String projCode = 'CJGKJEBYYB';
|
||||
@ -219,4 +221,16 @@ class GetServices {
|
||||
"/api/$projType/record/list?org_code=a&proj_code=$projCode&date=$date&dateEnd=$dateEnd"); //&tid=1000
|
||||
return res['data'];
|
||||
}
|
||||
|
||||
plumAdd(List<PointXY> list) async {
|
||||
//
|
||||
Map res = await service.postClient("/api/$projType/xx/add", {"list": list});
|
||||
return res;
|
||||
}
|
||||
|
||||
getPlumList(String name) async {
|
||||
Map res = await service.getClient(
|
||||
"/api/$projType/xx/list?proj_code=$projCode&name=$name"); //&tid=1000
|
||||
return res['data'];
|
||||
}
|
||||
}
|
||||
|
@ -1,155 +1,80 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'base.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import 'base.dart';
|
||||
|
||||
// String projCode = 'CJGKJEBYYB';
|
||||
// String tid = "109";
|
||||
// String projType = "hydraulic_tamping";
|
||||
String projCode = 'TEST';
|
||||
String tid = "1000";
|
||||
String projType = "pile_cm";
|
||||
BaseService service = BaseService();
|
||||
// // 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'];
|
||||
}
|
||||
// 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'];
|
||||
}
|
||||
// // 设备绑定 --- 获取设备
|
||||
// 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'];
|
||||
}
|
||||
}
|
||||
// // 项目列表
|
||||
// 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 {
|
||||
// 验证码
|
||||
getsmsCode(String phone) async {
|
||||
Map res =
|
||||
await service.postClient("/admin/base/open/smsCode", {"phone": phone});
|
||||
return res;
|
||||
}
|
||||
// class LoginController {
|
||||
// // 验证码
|
||||
// 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;
|
||||
}
|
||||
// // 手机号登录
|
||||
// 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'];
|
||||
}
|
||||
// // 获取用户信息
|
||||
// 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'];
|
||||
}
|
||||
// // 验证码
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// 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 {
|
||||
//获取水泥搅拌桩点数据
|
||||
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'];
|
||||
}
|
||||
|
||||
//获取水泥搅拌桩点数据
|
||||
// class PileCmController {
|
||||
// //获取水泥搅拌桩点数据
|
||||
// getRcordData(int page, int size, String date,
|
||||
// [String sort = "desc", String order = "pile_id"]) async {
|
||||
// Map res = await service.getClient(
|
||||
@ -163,82 +88,157 @@ class GetServices {
|
||||
// "/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 [];
|
||||
}
|
||||
}
|
||||
// //获取施工记录的日期
|
||||
// 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'];
|
||||
}
|
||||
// //施工详细记录
|
||||
// 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;
|
||||
}
|
||||
// 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 {};
|
||||
// }
|
||||
// }
|
||||
|
||||
// 手机号登录
|
||||
phoneLogin(String phone, String smsCode) async {
|
||||
Map res = await service.postClient(
|
||||
"/admin/base/open/phone", {"phone": phone, "smsCode": smsCode});
|
||||
return res;
|
||||
}
|
||||
// // 设备绑定 --- 获取设备
|
||||
// Future getDeviceBind() async {
|
||||
// Map res = await service.getClient(
|
||||
// '/api/sys/device_bind/list?proj_type=$projType&proj_code=$projCode');
|
||||
// return res['data'];
|
||||
// }
|
||||
|
||||
// 获取用户信息
|
||||
getPerson() async {
|
||||
Map res = await service.getClient("/admin/base/comm/person");
|
||||
return res['data'];
|
||||
}
|
||||
// // 项目列表
|
||||
// getproject() async {
|
||||
// Map res = await service
|
||||
// .getClient('/api/sys/project/list?org_code=a&proj_type=$projType');
|
||||
// return res['data'];
|
||||
// }
|
||||
|
||||
// 验证码
|
||||
getCaptcha() async {
|
||||
Map res =
|
||||
await service.getClient("/admin/base/open/captcha?height=40&width=150");
|
||||
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'];
|
||||
// }
|
||||
|
||||
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;
|
||||
}
|
||||
// //获取水泥搅拌桩点数据
|
||||
// // 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'];
|
||||
// // }
|
||||
|
||||
getRtuLast() async {
|
||||
Map res = await service.getClient(
|
||||
"/api/t2n/rtu/rtu_last?proj_type=$projType&proj_code=$projCode");
|
||||
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 [];
|
||||
// }
|
||||
// }
|
||||
|
||||
getCoordTrans() async {
|
||||
Map res = await service.getClient(
|
||||
"/api/comm/coord_trans/list?proj_type=$projType&proj_code=$projCode");
|
||||
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'];
|
||||
// }
|
||||
|
||||
// // 验证码
|
||||
// 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'];
|
||||
// }
|
||||
// }
|
||||
|
@ -1,74 +0,0 @@
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
import 'package:cpnav/models/user.dart';
|
||||
|
||||
class LoginPrefs {
|
||||
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 {
|
||||
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();
|
||||
}
|
||||
}
|
29
plugins/quick_blue/.gitignore
vendored
Normal 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/
|
10
plugins/quick_blue/.metadata
Normal 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
|
3
plugins/quick_blue/CHANGELOG.md
Normal file
@ -0,0 +1,3 @@
|
||||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
1
plugins/quick_blue/LICENSE
Normal file
@ -0,0 +1 @@
|
||||
TODO: Add your license here.
|
109
plugins/quick_blue/README.md
Normal 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);
|
||||
```
|
4
plugins/quick_blue/analysis_options.yaml
Normal 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
@ -0,0 +1,8 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
50
plugins/quick_blue/android/build.gradle
Normal 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"
|
||||
}
|
1
plugins/quick_blue/android/settings.gradle
Normal file
@ -0,0 +1 @@
|
||||
rootProject.name = 'quick_blue'
|
12
plugins/quick_blue/android/src/main/AndroidManifest.xml
Normal 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>
|
@ -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)
|
||||
}
|
281
plugins/quick_blue/darwin/QuickBlueDarwin.swift
Normal 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
@ -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
|
10
plugins/quick_blue/example/.metadata
Normal 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
|
16
plugins/quick_blue/example/README.md
Normal 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.
|
29
plugins/quick_blue/example/analysis_options.yaml
Normal 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
|
13
plugins/quick_blue/example/android/.gitignore
vendored
Normal 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
|
68
plugins/quick_blue/example/android/app/build.gradle
Normal 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"
|
||||
}
|
@ -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>
|
@ -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>
|
@ -0,0 +1,6 @@
|
||||
package com.example.quick_blue_example
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
}
|
@ -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>
|
@ -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>
|
After Width: | Height: | Size: 544 B |
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 721 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
@ -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>
|
@ -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>
|
@ -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>
|
31
plugins/quick_blue/example/android/build.gradle
Normal 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
|
||||
}
|
3
plugins/quick_blue/example/android/gradle.properties
Normal file
@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
6
plugins/quick_blue/example/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal 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
|
11
plugins/quick_blue/example/android/settings.gradle
Normal 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"
|
34
plugins/quick_blue/example/ios/.gitignore
vendored
Normal 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
|
@ -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>
|
2
plugins/quick_blue/example/ios/Flutter/Debug.xcconfig
Normal file
@ -0,0 +1,2 @@
|
||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "Generated.xcconfig"
|
2
plugins/quick_blue/example/ios/Flutter/Release.xcconfig
Normal file
@ -0,0 +1,2 @@
|
||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "Generated.xcconfig"
|
41
plugins/quick_blue/example/ios/Podfile
Normal 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
|
552
plugins/quick_blue/example/ios/Runner.xcodeproj/project.pbxproj
Normal 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 */;
|
||||
}
|
7
plugins/quick_blue/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
@ -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>
|
@ -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>
|
@ -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>
|
10
plugins/quick_blue/example/ios/Runner.xcworkspace/contents.xcworkspacedata
generated
Normal 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>
|
@ -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>
|
@ -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>
|
13
plugins/quick_blue/example/ios/Runner/AppDelegate.swift
Normal 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)
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 564 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.5 KiB |