实时历史设置

This commit is contained in:
tanlinxing 2024-08-29 17:45:39 +08:00
parent 569d7bb948
commit a139836df5
192 changed files with 10728 additions and 513 deletions

View File

@ -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\\

File diff suppressed because one or more lines are too long

View File

@ -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),
),

View File

@ -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 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,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
return ValueListenableBuilder<bool>(
valueListenable: MyHomePage.isDarkMode,
builder: (context, isDarkMode, child) {
return MaterialApp(
title: 'Flutter Demo',
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),
),

View File

@ -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();
}
}

View File

@ -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';

View 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;
}
}

View 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))
],
)),
);
}
}

View 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('暂无数据'))),
);
}
}

View 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());
});
}
}

View 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,
);
}

View File

@ -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});

View File

@ -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();
}
}

View 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
View 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,
),
);
}
});
}
}

View 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))))
],
);
}
}

View 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
]),
));
}
}

View 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);
});
}
}

View File

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

View File

@ -0,0 +1,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("更新设置"))
],
);
},
),
));
}
}

View File

@ -0,0 +1,344 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package: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),
);
}
}

View File

@ -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) {
//
}
}
}

View File

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

View File

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

View File

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

View File

@ -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;
}

View File

@ -0,0 +1,252 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:wifi_iot/wifi_iot.dart';
import 'dart:async';
import 'package:wifi_scan/wifi_scan.dart';
import '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)));
}

View File

@ -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(), // 线
],
);
},
));
}
}

View 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);
}

View 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(), // 线
],
);
},
);
}
}

View File

@ -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';
@ -216,7 +218,19 @@ class GetServices {
getRcordList(String date, [String? dateEnd, int? tid]) async {
dateEnd ??= date;
Map res = await service.getClient(
"/api/$projType/record/list?org_code=a&proj_code=$projCode&date=$date&dateEnd=$dateEnd");//&tid=1000
"/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'];
}
}

View File

@ -1,244 +1,244 @@
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'];
}
// 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'];
}
// 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 [];
}
}
// //
// 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'];
// }
// }
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 {};
}
}
// 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'];
}
// // ---
// 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'];
}
// //
// getproject() async {
// Map res = await service
// .getClient('/api/sys/project/list?org_code=a&proj_type=$projType');
// return res['data'];
// }
//
getRcordData(int page, int size, String date,
[String sort = "desc", String order = "tp_id"]) async {
Map res = await service.getClient(
"/api/$projType/record/page?page=$page&size=$size&org_code=a&proj_code=$projCode&tid=$tid&date=$date&sort=$sort&order=$order");
return res['data'];
}
// //
// getRcordData(int page, int size, String date,
// [String sort = "desc", String order = "tp_id"]) async {
// Map res = await service.getClient(
// "/api/$projType/record/page?page=$page&size=$size&org_code=a&proj_code=$projCode&tid=$tid&date=$date&sort=$sort&order=$order");
// return res['data'];
// }
//
// getRcordData(int page, int size, String date,
// [String sort = "desc", String order = "pile_id"]) async {
// Map res = await service.getClient(
// "/api/$projType/record/page?page=$page&size=$size&org_code=a&proj_code=$projCode&tid=$tid&date=$date&sort=$sort&order=$order");
// return res['data'];
// }
// //
// // getRcordData(int page, int size, String date,
// // [String sort = "desc", String order = "pile_id"]) async {
// // Map res = await service.getClient(
// // "/api/$projType/record/page?page=$page&size=$size&org_code=a&proj_code=$projCode&tid=$tid&date=$date&sort=$sort&order=$order");
// // return res['data'];
// // }
// getRcordList(String date, String? dateEnd) async {
// dateEnd ??= date;
// Map res = await service.getClient(
// "/api/$projType/record/list?org_code=a&proj_code=$projCode&tid=$tid&date=$date&dateEnd=$dateEnd");
// return res['data'];
// }
getRcordList(String date, [String? dateEnd]) async {
dateEnd ??= date;
Map res = await service.getClient(
"/api/$projType/record/list?org_code=a&proj_code=$projCode&tid=$tid&date=$date&dateEnd=$dateEnd");
if (res['code'] == 1000) {
return res['data'];
} else {
return [];
}
}
// // getRcordList(String date, String? dateEnd) async {
// // dateEnd ??= date;
// // Map res = await service.getClient(
// // "/api/$projType/record/list?org_code=a&proj_code=$projCode&tid=$tid&date=$date&dateEnd=$dateEnd");
// // return res['data'];
// // }
// getRcordList(String date, [String? dateEnd]) async {
// dateEnd ??= date;
// Map res = await service.getClient(
// "/api/$projType/record/list?org_code=a&proj_code=$projCode&tid=$tid&date=$date&dateEnd=$dateEnd");
// if (res['code'] == 1000) {
// return res['data'];
// } else {
// return [];
// }
// }
//
getworkDateData() async {
Map res = await service.getClient(
"/api/$projType/record/work_date?org_code=a&proj_code=$projCode&tid=$tid");
if (res['code'] == 1000) {
return res['data'] ?? [];
} else {
return [];
}
}
// //
// 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;
}
// //
// 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;
// }
getRtuLast() async {
Map res = await service.getClient(
"/api/t2n/rtu/rtu_last?proj_type=$projType&proj_code=$projCode");
return res['data'];
}
// 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'];
}
}
// getCoordTrans() async {
// Map res = await service.getClient(
// "/api/comm/coord_trans/list?proj_type=$projType&proj_code=$projCode");
// return res['data'];
// }
// }

View File

@ -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
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

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