initial commit
This commit is contained in:
2
lib/xiao_connector/constants/constants.dart
Normal file
2
lib/xiao_connector/constants/constants.dart
Normal file
@@ -0,0 +1,2 @@
|
||||
const uuidLSM6DS3TRService = '4c534d36-4453-3354-5253-657276696365';
|
||||
const uuidAccelerationData = '61636365-6c65-7261-7469-6f6e44617461';
|
||||
280
lib/xiao_connector/cubit/xiao_connector_cubit.dart
Normal file
280
lib/xiao_connector/cubit/xiao_connector_cubit.dart
Normal file
@@ -0,0 +1,280 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_blue_plus_windows/flutter_blue_plus_windows.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:xiao_pet_tracker/bootstrap.dart';
|
||||
import 'package:xiao_pet_tracker/objectbox.dart';
|
||||
import 'package:xiao_pet_tracker/objectbox.g.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/models/capture_box.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/models/capture_point.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/utils/utils.dart';
|
||||
|
||||
part 'xiao_connector_state.dart';
|
||||
|
||||
class XiaoConnectorCubit extends Cubit<XiaoConnectorState> {
|
||||
XiaoConnectorCubit() : super(const XiaoConnectorState());
|
||||
|
||||
late BluetoothDevice device;
|
||||
List<BluetoothService> _services = [];
|
||||
late BluetoothCharacteristic _senseService;
|
||||
|
||||
late StreamSubscription<List<int>> dataCapturingSubscription;
|
||||
|
||||
final ObjectBox _objectBox = getIt<ObjectBox>();
|
||||
late final Box<CapturePoint> _capturePointsBox;
|
||||
late final Box<CaptureBox> _captureBoxes;
|
||||
|
||||
bool gotRotation = false;
|
||||
bool gotAcceleration = false;
|
||||
bool gotTimeStamp = false;
|
||||
|
||||
int _rotX = 0;
|
||||
int _rotY = 0;
|
||||
int _rotZ = 0;
|
||||
int _accX = 0;
|
||||
int _accY = 0;
|
||||
int _accZ = 0;
|
||||
int _millisecondsSinceEpochSend = 0;
|
||||
String _captureType = '';
|
||||
String _uuid = '';
|
||||
|
||||
bool isRecording = false;
|
||||
|
||||
Future<void> init() async {
|
||||
_capturePointsBox = _objectBox.store.box<CapturePoint>();
|
||||
_captureBoxes = _objectBox.store.box<CaptureBox>();
|
||||
}
|
||||
|
||||
Future<void> connect() async {
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.loading));
|
||||
|
||||
try {
|
||||
// scan for devices for 15 seconds
|
||||
await FlutterBluePlus.startScan(timeout: const Duration(seconds: 15));
|
||||
} catch (e) {
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.failure));
|
||||
throw Exception('Start Scan Error: $e');
|
||||
}
|
||||
|
||||
FlutterBluePlus.scanResults.listen(
|
||||
(results) async {
|
||||
for (final r in results) {
|
||||
if (kDebugMode) {
|
||||
print(r);
|
||||
}
|
||||
|
||||
// set the xiao sense name
|
||||
if (r.device.advName == 'Go Bluetooth 2') {
|
||||
await FlutterBluePlus.stopScan();
|
||||
device = r.device;
|
||||
|
||||
await device.connect();
|
||||
|
||||
try {
|
||||
_services = await device.discoverServices();
|
||||
} catch (e) {
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.failure));
|
||||
throw Exception('Discover Services Error: $e');
|
||||
}
|
||||
|
||||
device.connectionState
|
||||
.listen((BluetoothConnectionState blueState) async {
|
||||
if (blueState == BluetoothConnectionState.disconnected) {
|
||||
debugPrint('DISCONNECTED!');
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.initial));
|
||||
}
|
||||
});
|
||||
|
||||
// device.cancelWhenDisconnected(deviceConnectionStream);
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.connected));
|
||||
}
|
||||
}
|
||||
},
|
||||
onDone: () {
|
||||
debugPrint('DONE');
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.initial));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setCapturingOn() async {
|
||||
final senseService = _services
|
||||
.where(
|
||||
(s) => s.serviceUuid == Guid('4c534d36-4453-3354-5253-657276696365'),
|
||||
)
|
||||
.first
|
||||
.characteristics
|
||||
.where(
|
||||
(c) =>
|
||||
c.characteristicUuid ==
|
||||
Guid('63617074-7572-696E-6753-657276696365'),
|
||||
)
|
||||
.first;
|
||||
|
||||
await senseService.write([1]);
|
||||
}
|
||||
|
||||
Uint8List int64BigEndianBytes(int value) =>
|
||||
Uint8List(8)..buffer.asByteData().setInt64(0, value);
|
||||
|
||||
Future<void> setTime(int millisSinceEpoch) async {
|
||||
final senseService = _services
|
||||
.where(
|
||||
(s) => s.serviceUuid == Guid('4c534d36-4453-3354-5253-657276696365'),
|
||||
)
|
||||
.first
|
||||
.characteristics
|
||||
.where(
|
||||
(c) =>
|
||||
c.characteristicUuid ==
|
||||
Guid('756E6978-5469-6D65-5374-616D70527374'),
|
||||
)
|
||||
.first;
|
||||
|
||||
final List<int> bytes = int64BigEndianBytes(millisSinceEpoch);
|
||||
await senseService.write(bytes);
|
||||
}
|
||||
|
||||
Future<void> startCapturing() async {
|
||||
_senseService = _services
|
||||
.where(
|
||||
(s) => s.serviceUuid == Guid('4c534d36-4453-3354-5253-657276696365'),
|
||||
)
|
||||
.first
|
||||
.characteristics
|
||||
.where(
|
||||
(c) =>
|
||||
c.characteristicUuid ==
|
||||
Guid('61636365-6c65-7261-7469-6f6e44617461'),
|
||||
)
|
||||
.first;
|
||||
|
||||
dataCapturingSubscription = _senseService.onValueReceived.listen((value) {
|
||||
// rotation
|
||||
if (value.last == 1) {
|
||||
_rotX = fromBytesToInt32(value[0], value[1], value[2], value[3]);
|
||||
_rotY = fromBytesToInt32(value[4], value[5], value[6], value[7]);
|
||||
_rotZ = fromBytesToInt32(value[8], value[9], value[10], value[11]);
|
||||
gotRotation = true;
|
||||
}
|
||||
|
||||
// acceleration
|
||||
if (value.last == 2) {
|
||||
_accX = fromBytesToInt32(value[0], value[1], value[2], value[3]);
|
||||
_accY = fromBytesToInt32(value[4], value[5], value[6], value[7]);
|
||||
_accZ = fromBytesToInt32(value[8], value[9], value[10], value[11]);
|
||||
gotAcceleration = true;
|
||||
}
|
||||
|
||||
if (value.last == 3) {
|
||||
final timeStamp = fromBytesToInt64(
|
||||
value[0],
|
||||
value[1],
|
||||
value[2],
|
||||
value[3],
|
||||
value[4],
|
||||
value[5],
|
||||
value[6],
|
||||
value[7],
|
||||
);
|
||||
_millisecondsSinceEpochSend = timeStamp;
|
||||
gotTimeStamp = true;
|
||||
}
|
||||
|
||||
if (gotAcceleration && gotRotation && gotTimeStamp) {
|
||||
final capturePoint = CapturePoint(
|
||||
type: _captureType,
|
||||
uuid: _uuid,
|
||||
rotationX: _rotX,
|
||||
rotationY: _rotY,
|
||||
rotationZ: _rotZ,
|
||||
accelerationX: _accX,
|
||||
accelerationY: _accY,
|
||||
accelerationZ: _accZ,
|
||||
millisecondsSinceEpochReceived:
|
||||
DateTime.now().toUtc().millisecondsSinceEpoch,
|
||||
millisecondsSinceEpochSend: _millisecondsSinceEpochSend,
|
||||
);
|
||||
emit(state.copyWith(lastCapturedPoint: capturePoint));
|
||||
|
||||
if (isRecording) {
|
||||
_writeToObjectBox(capturePoint: capturePoint);
|
||||
}
|
||||
|
||||
gotAcceleration = false;
|
||||
gotRotation = false;
|
||||
gotTimeStamp = false;
|
||||
}
|
||||
});
|
||||
await setCapturingOn();
|
||||
|
||||
await setTime(DateTime.now().millisecondsSinceEpoch);
|
||||
|
||||
device.cancelWhenDisconnected(dataCapturingSubscription);
|
||||
|
||||
await _senseService.setNotifyValue(true);
|
||||
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.capturing));
|
||||
}
|
||||
|
||||
Future<void> stopCapturing() async {
|
||||
await dataCapturingSubscription.cancel();
|
||||
|
||||
await _senseService.setNotifyValue(false);
|
||||
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.connected));
|
||||
}
|
||||
|
||||
Future<void> disconnectDevice() async {
|
||||
await device.disconnect();
|
||||
_services = [];
|
||||
emit(state.copyWith(status: XiaoConnectorStatus.initial));
|
||||
}
|
||||
|
||||
void toggleRecording({required String captureType}) {
|
||||
isRecording = !isRecording;
|
||||
if (isRecording) {
|
||||
_captureType = captureType;
|
||||
_uuid = const Uuid().v4();
|
||||
_captureBoxes.put(
|
||||
CaptureBox(
|
||||
type: _captureType,
|
||||
uuid: _uuid,
|
||||
startTime: DateTime.now(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _writeToObjectBox({
|
||||
required CapturePoint capturePoint,
|
||||
}) {
|
||||
_capturePointsBox.put(capturePoint);
|
||||
}
|
||||
|
||||
List<CapturePoint> getCapturePointsOfUuid(String uuid) {
|
||||
final query =
|
||||
_capturePointsBox.query(CapturePoint_.uuid.equals(uuid)).build();
|
||||
|
||||
final points = query.find();
|
||||
return points;
|
||||
}
|
||||
|
||||
void deleteRecording(String uuid) {
|
||||
_capturePointsBox.query(CapturePoint_.uuid.equals(uuid)).build().remove();
|
||||
_captureBoxes.query(CaptureBox_.uuid.equals(uuid)).build().remove();
|
||||
}
|
||||
|
||||
List<CaptureBox> getAllCaptureBoxes() {
|
||||
final query = (_captureBoxes.query()
|
||||
..order(CaptureBox_.startTime, flags: Order.descending))
|
||||
.build();
|
||||
final boxes = query.find();
|
||||
return boxes;
|
||||
}
|
||||
|
||||
List<CapturePoint> getAllCapturePoints() => _capturePointsBox.getAll();
|
||||
}
|
||||
40
lib/xiao_connector/cubit/xiao_connector_state.dart
Normal file
40
lib/xiao_connector/cubit/xiao_connector_state.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
part of 'xiao_connector_cubit.dart';
|
||||
|
||||
enum XiaoConnectorStatus {
|
||||
initial,
|
||||
loading,
|
||||
connected,
|
||||
capturing,
|
||||
failure,
|
||||
}
|
||||
|
||||
extension XiaoConnectorStatusX on XiaoConnectorStatus {
|
||||
bool get isInitial => this == XiaoConnectorStatus.initial;
|
||||
bool get isLoading => this == XiaoConnectorStatus.loading;
|
||||
bool get isConnected => this == XiaoConnectorStatus.connected;
|
||||
bool get isCapturing => this == XiaoConnectorStatus.capturing;
|
||||
bool get isFailure => this == XiaoConnectorStatus.failure;
|
||||
}
|
||||
|
||||
final class XiaoConnectorState extends Equatable {
|
||||
const XiaoConnectorState({
|
||||
this.status = XiaoConnectorStatus.initial,
|
||||
this.lastCapturedPoint,
|
||||
});
|
||||
|
||||
final XiaoConnectorStatus status;
|
||||
final CapturePoint? lastCapturedPoint;
|
||||
|
||||
XiaoConnectorState copyWith({
|
||||
XiaoConnectorStatus? status,
|
||||
CapturePoint? lastCapturedPoint,
|
||||
}) {
|
||||
return XiaoConnectorState(
|
||||
status: status ?? this.status,
|
||||
lastCapturedPoint: lastCapturedPoint ?? this.lastCapturedPoint,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [status, lastCapturedPoint];
|
||||
}
|
||||
18
lib/xiao_connector/models/capture_box.dart
Normal file
18
lib/xiao_connector/models/capture_box.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
import 'package:objectbox/objectbox.dart';
|
||||
|
||||
@Entity()
|
||||
class CaptureBox {
|
||||
CaptureBox({
|
||||
required this.type,
|
||||
required this.uuid,
|
||||
required this.startTime,
|
||||
});
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
String type;
|
||||
String uuid;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime startTime;
|
||||
}
|
||||
32
lib/xiao_connector/models/capture_point.dart
Normal file
32
lib/xiao_connector/models/capture_point.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'package:objectbox/objectbox.dart';
|
||||
|
||||
@Entity()
|
||||
class CapturePoint {
|
||||
CapturePoint({
|
||||
required this.type,
|
||||
required this.uuid,
|
||||
required this.rotationX,
|
||||
required this.rotationY,
|
||||
required this.rotationZ,
|
||||
required this.accelerationX,
|
||||
required this.accelerationY,
|
||||
required this.accelerationZ,
|
||||
required this.millisecondsSinceEpochReceived,
|
||||
required this.millisecondsSinceEpochSend,
|
||||
});
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
String type;
|
||||
String uuid;
|
||||
|
||||
int rotationX;
|
||||
int rotationY;
|
||||
int rotationZ;
|
||||
int accelerationX;
|
||||
int accelerationY;
|
||||
int accelerationZ;
|
||||
|
||||
int millisecondsSinceEpochReceived;
|
||||
int millisecondsSinceEpochSend;
|
||||
}
|
||||
32
lib/xiao_connector/utils/utils.dart
Normal file
32
lib/xiao_connector/utils/utils.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
int fromBytesToInt32(int b3, int b2, int b1, int b0) {
|
||||
final int8List = Int8List(4)
|
||||
..[3] = b3
|
||||
..[2] = b2
|
||||
..[1] = b1
|
||||
..[0] = b0;
|
||||
return int8List.buffer.asByteData().getInt32(0);
|
||||
}
|
||||
|
||||
int fromBytesToInt64(
|
||||
int b7,
|
||||
int b6,
|
||||
int b5,
|
||||
int b4,
|
||||
int b3,
|
||||
int b2,
|
||||
int b1,
|
||||
int b0,
|
||||
) {
|
||||
final int8List = Int8List(8)
|
||||
..[7] = b7
|
||||
..[6] = b6
|
||||
..[5] = b5
|
||||
..[4] = b4
|
||||
..[3] = b3
|
||||
..[2] = b2
|
||||
..[1] = b1
|
||||
..[0] = b0;
|
||||
return int8List.buffer.asByteData().getInt64(0);
|
||||
}
|
||||
193
lib/xiao_connector/view/capture_view.dart
Normal file
193
lib/xiao_connector/view/capture_view.dart
Normal file
@@ -0,0 +1,193 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/cubit/xiao_connector_cubit.dart';
|
||||
|
||||
class CaptureView extends StatefulWidget {
|
||||
const CaptureView({super.key});
|
||||
|
||||
@override
|
||||
State<CaptureView> createState() => _CaptureViewState();
|
||||
}
|
||||
|
||||
class _CaptureViewState extends State<CaptureView> {
|
||||
late TextEditingController _controller;
|
||||
bool _isTextFieldFocused = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TextEditingController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// final count = context.select((XiaoConnectorCubit cubit) => cubit.state);
|
||||
final lastCapturePoint = context
|
||||
.select((XiaoConnectorCubit cubit) => cubit.state.lastCapturedPoint);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Live Feed'),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context.read<XiaoConnectorCubit>().stopCapturing();
|
||||
},
|
||||
child: const Text('Close Live Feed'),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
const Text(
|
||||
'Last Captured Point',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Divider(
|
||||
indent: 80,
|
||||
endIndent: 80,
|
||||
),
|
||||
const Text(
|
||||
'Received Time',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
'${DateTime.fromMillisecondsSinceEpoch(
|
||||
lastCapturePoint?.millisecondsSinceEpochReceived ?? 0,
|
||||
)}',
|
||||
),
|
||||
const Text(
|
||||
'Send Time',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Text('(time the controller send the data)'),
|
||||
Text(
|
||||
'${DateTime.fromMillisecondsSinceEpoch(
|
||||
lastCapturePoint?.millisecondsSinceEpochSend ?? 0,
|
||||
)}',
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
// context.read<XiaoConnectorCubit>().startCapturing(
|
||||
// captureType: 'test',
|
||||
// );
|
||||
final timeToSend = DateTime.now().millisecondsSinceEpoch;
|
||||
context.read<XiaoConnectorCubit>().setTime(timeToSend);
|
||||
},
|
||||
icon: const Icon(Icons.watch_later_outlined),
|
||||
label: const Text('Sync Clocks'),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
const Divider(
|
||||
indent: 80,
|
||||
endIndent: 80,
|
||||
),
|
||||
Text('Acceleration X: ${lastCapturePoint?.accelerationX}'),
|
||||
Text('Acceleration Y: ${lastCapturePoint?.accelerationY}'),
|
||||
Text('Acceleration Z: ${lastCapturePoint?.accelerationZ}'),
|
||||
const Divider(
|
||||
indent: 80,
|
||||
endIndent: 80,
|
||||
),
|
||||
Text('Rotation X: ${lastCapturePoint?.rotationX}'),
|
||||
Text('Rotation Y: ${lastCapturePoint?.rotationY}'),
|
||||
Text('Rotation Z: ${lastCapturePoint?.rotationZ}'),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 32, right: 32, top: 16),
|
||||
child: FocusScope(
|
||||
child: Focus(
|
||||
onFocusChange: (focus) {
|
||||
_isTextFieldFocused = focus;
|
||||
setState(() {});
|
||||
},
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
decoration: const InputDecoration(
|
||||
label: Text('Capture Name'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||
floatingActionButton: _isTextFieldFocused
|
||||
? null
|
||||
: FloatingActionButton.large(
|
||||
child: context.read<XiaoConnectorCubit>().isRecording
|
||||
? const Stack(
|
||||
children: [
|
||||
Align(
|
||||
child: Icon(Icons.pause),
|
||||
),
|
||||
Align(
|
||||
child: SizedBox.expand(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const Icon(Icons.play_arrow),
|
||||
onPressed: () {
|
||||
if (_controller.value.text == '') {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text(
|
||||
'Please enter Capture Name',
|
||||
),
|
||||
content: const Text(
|
||||
'You need to enter a capture name before you can start capturing data.',
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text(
|
||||
'Ok',
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
context.read<XiaoConnectorCubit>().toggleRecording(
|
||||
captureType: _controller.value.text,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
51
lib/xiao_connector/view/connected_view.dart
Normal file
51
lib/xiao_connector/view/connected_view.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/cubit/xiao_connector_cubit.dart';
|
||||
|
||||
class ConnectedView extends StatelessWidget {
|
||||
const ConnectedView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Xiao Connector'),
|
||||
),
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text(
|
||||
'''
|
||||
You are connected to the Xiao Sense.
|
||||
Now you can open the live feed.''',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 48,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context.read<XiaoConnectorCubit>().startCapturing();
|
||||
},
|
||||
child: const Text('Open Live Feed'),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 48,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context.read<XiaoConnectorCubit>().disconnectDevice();
|
||||
},
|
||||
child: const Text('Disconnect'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
10
lib/xiao_connector/view/failure_view.dart
Normal file
10
lib/xiao_connector/view/failure_view.dart
Normal file
@@ -0,0 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FailureView extends StatelessWidget {
|
||||
const FailureView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Text('Oops. Something bad happened! :(');
|
||||
}
|
||||
}
|
||||
38
lib/xiao_connector/view/initial_view.dart
Normal file
38
lib/xiao_connector/view/initial_view.dart
Normal file
@@ -0,0 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/cubit/xiao_connector_cubit.dart';
|
||||
|
||||
class InitialView extends StatelessWidget {
|
||||
const InitialView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Xiao Connector'),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text(
|
||||
'''
|
||||
You are currently not connected to the Xiao Sense.
|
||||
Click on `Connect` to try to connect to the device.''',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context.read<XiaoConnectorCubit>().connect();
|
||||
},
|
||||
child: const Text('Connect'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
12
lib/xiao_connector/view/loading_view.dart
Normal file
12
lib/xiao_connector/view/loading_view.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LoadingView extends StatelessWidget {
|
||||
const LoadingView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
}
|
||||
1
lib/xiao_connector/view/view.dart
Normal file
1
lib/xiao_connector/view/view.dart
Normal file
@@ -0,0 +1 @@
|
||||
export 'xiao_connector_page.dart';
|
||||
40
lib/xiao_connector/view/xiao_connector_page.dart
Normal file
40
lib/xiao_connector/view/xiao_connector_page.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/cubit/xiao_connector_cubit.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/view/capture_view.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/view/connected_view.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/view/failure_view.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/view/initial_view.dart';
|
||||
import 'package:xiao_pet_tracker/xiao_connector/view/loading_view.dart';
|
||||
|
||||
@RoutePage()
|
||||
class XiaoConnectorPage extends StatelessWidget {
|
||||
const XiaoConnectorPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const XiaoConnectorView();
|
||||
}
|
||||
}
|
||||
|
||||
class XiaoConnectorView extends StatelessWidget {
|
||||
const XiaoConnectorView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: BlocBuilder<XiaoConnectorCubit, XiaoConnectorState>(
|
||||
builder: (context, state) {
|
||||
return switch (state.status) {
|
||||
XiaoConnectorStatus.initial => const InitialView(),
|
||||
XiaoConnectorStatus.loading => const LoadingView(),
|
||||
XiaoConnectorStatus.connected => const ConnectedView(),
|
||||
XiaoConnectorStatus.capturing => const CaptureView(),
|
||||
XiaoConnectorStatus.failure => const FailureView(),
|
||||
};
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
22
lib/xiao_connector/view/xiao_data_page.dart
Normal file
22
lib/xiao_connector/view/xiao_data_page.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@RoutePage()
|
||||
class XiaoDataPage extends StatelessWidget {
|
||||
const XiaoDataPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Placeholder();
|
||||
}
|
||||
}
|
||||
|
||||
class XiaoDataView extends StatelessWidget {
|
||||
const XiaoDataView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Placeholder();
|
||||
}
|
||||
}
|
||||
1
lib/xiao_connector/xiao_connector.dart
Normal file
1
lib/xiao_connector/xiao_connector.dart
Normal file
@@ -0,0 +1 @@
|
||||
export 'view/view.dart';
|
||||
Reference in New Issue
Block a user