0% found this document useful (0 votes)
8 views8 pages

App Code Flutter

The Moto■Puck Arrows App is a Flutter application that connects to BLE devices to send directional commands for navigation. It includes features like device scanning, connection management, and command sending for LEFT, RIGHT, STRAIGHT, UTURN, and ARRIVED. The document provides setup instructions, required permissions, and code implementation details for both Android and iOS platforms.

Uploaded by

abhilash
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views8 pages

App Code Flutter

The Moto■Puck Arrows App is a Flutter application that connects to BLE devices to send directional commands for navigation. It includes features like device scanning, connection management, and command sending for LEFT, RIGHT, STRAIGHT, UTURN, and ARRIVED. The document provides setup instructions, required permissions, and code implementation details for both Android and iOS platforms.

Uploaded by

abhilash
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Moto■Puck Arrows App (Flutter) — Complete Code

This PDF contains a complete Flutter app you can run on BOTH Android and iOS. It connects to your BLE
device and sends arrow commands like LEFT/RIGHT/STRAIGHT/UTURN/ARRIVED. This is an MVP
controller app (not full Google Maps routing).

What this app does


• Scan BLE devices
• Connect to your Moto■Puck
• Find a writable BLE characteristic
• Send text commands to the device: LEFT, RIGHT, STRAIGHT, UTURN, ARRIVED
• Optional: send SPEED:42, ETA:18

Before you start (requirements)


1) Install Flutter SDK
2) Install Android Studio (Android) and Xcode (iOS)
3) Use a real phone (BLE is unreliable on emulator)
4) Turn on Bluetooth + Location (Android needs it for scanning)

Flutter dependencies
We use flutter_blue_plus because it’s popular and works on both Android and iOS.
name: moto_puck_app
description: Arrows-only BLE controller app for Moto-Puck prototype
publish_to: "none"
version: 1.0.0+1

environment:
sdk: ">=3.3.0 <4.0.0"

dependencies:
flutter:
sdk: flutter

flutter_blue_plus: ^1.34.5

cupertino_icons: ^1.0.6

dev_dependencies:
flutter_test:
sdk: flutter

flutter:
uses-material-design: true

Android permissions (android/app/src/main/[Link])


<!-- Add inside <manifest> -->
<uses-permission android:name="[Link]" />
<uses-permission android:name="[Link].BLUETOOTH_ADMIN" />

<!-- Android 12+ -->


<uses-permission android:name="[Link].BLUETOOTH_SCAN" />
<uses-permission android:name="[Link].BLUETOOTH_CONNECT" />

<!-- Needed for BLE scan results on many Android versions -->
<uses-permission android:name="[Link].ACCESS_FINE_LOCATION" />

<!-- Optional: if you want scan to work without location accuracy prompt -->
<uses-permission android:name="[Link].ACCESS_COARSE_LOCATION" />

iOS permissions (ios/Runner/[Link])


<!-- Add inside <dict> -->
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to connect to the Moto-Puck navigation display.</string>

<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app uses Bluetooth to connect to the Moto-Puck navigation display.</string>
MAIN APP CODE (lib/[Link])
import 'dart:async';
import 'dart:convert';
import 'package:flutter/[Link]';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';

void main() {
[Link]();
runApp(const MotoPuckApp());
}

class MotoPuckApp extends StatelessWidget {


const MotoPuckApp({[Link]});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Moto-Puck Arrows',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: [Link],
),
home: const HomePage(),
);
}
}

class HomePage extends StatefulWidget {


const HomePage({[Link]});

@override
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {


final FlutterBluePlus _ble = [Link];

bool _isScanning = false;


List<ScanResult> _scanResults = [];

BluetoothDevice? _device;
BluetoothCharacteristic? _writeChar;

String _status = "Not connected";


String _lastSent = "-";

StreamSubscription<List<ScanResult>>? _scanSub;
StreamSubscription<BluetoothConnectionState>? _connSub;

@override
void dispose() {
_scanSub?.cancel();
_connSub?.cancel();
[Link]();
}

Future<void> _startScan() async {


setState(() {
_scanResults = [];
_isScanning = true;
_status = "Scanning...";
});

// Stop any previous scan


await [Link]();

_scanSub?.cancel();
_scanSub = [Link]((results) {
setState(() {
_scanResults = results;
});
});

// Start scan
await [Link](timeout: const Duration(seconds: 8));

setState(() {
_isScanning = false;
_status = "Scan complete. Tap a device to connect.";
});
}

Future<void> _connectToDevice(BluetoothDevice device) async {


setState(() {
_status = "Connecting to ${[Link]}...";
_device = device;
_writeChar = null;
});

try {
await [Link](timeout: const Duration(seconds: 12), autoConnect: false);
} catch (e) {
// If already connected, ignore
}

_connSub?.cancel();
_connSub = [Link]((state) async {
if (!mounted) return;

if (state == [Link]) {
setState(() {
_status = "Connected. Discovering services...";
});
await _discoverServices(device);
} else if (state == [Link]) {
setState(() {
_status = "Disconnected";
_writeChar = null;
});
}
});

// Immediately try discovery too


await _discoverServices(device);
}

Future<void> _discoverServices(BluetoothDevice device) async {


try {
final services = await [Link]();

BluetoothCharacteristic? found;

// We search for ANY writable characteristic (MVP)


for (final s in services) {
for (final c in [Link]) {
final canWrite = [Link] || [Link];
if (canWrite) {
found = c;
break;
}
}
if (found != null) break;
}

if (found == null) {
setState(() {
_status = "Connected, but no writable characteristic found.";
_writeChar = null;
});
return;
}
setState(() {
_writeChar = found;
_status = "Ready! Send arrows now.";
});
} catch (e) {
setState(() {
_status = "Service discovery failed: $e";
});
}
}

Future<void> _disconnect() async {


if (_device == null) return;
try {
await _device!.disconnect();
} catch (_) {}
setState(() {
_status = "Disconnected";
_writeChar = null;
_device = null;
});
}

Future<void> _sendCommand(String cmd) async {


if (_device == null || _writeChar == null) {
setState(() {
_status = "Not ready. Connect to device first.";
});
return;
}

try {
final bytes = [Link](cmd);

// Prefer writeWithoutResponse if available for speed


final withoutResponse = _writeChar!.[Link];

await _writeChar!.write(bytes, withoutResponse: withoutResponse);

setState(() {
_lastSent = cmd;
_status = "Sent: $cmd";
});
} catch (e) {
setState(() {
_status = "Send failed: $e";
});
}
}

Widget _deviceTile(ScanResult r) {
final name = [Link] ? [Link] : "(Unnamed)";
final id = [Link];

return Card(
child: ListTile(
title: Text(name),
subtitle: Text(id),
trailing: Text("${[Link]} dBm"),
onTap: () => _connectToDevice([Link]),
),
);
}

Widget _commandButton(String label, String cmd, IconData icon) {


return Expanded(
child: Padding(
padding: const [Link](6),
child: [Link](
style: [Link](
padding: const [Link](vertical: 14),
),
onPressed: () => _sendCommand(cmd),
icon: Icon(icon),
label: Text(label),
),
),
);
}

@override
Widget build(BuildContext context) {
final connectedName = _device?.platformName ?? "-";

return Scaffold(
appBar: AppBar(
title: const Text("Moto■Puck Arrows"),
actions: [
if (_device != null)
IconButton(
onPressed: _disconnect,
icon: const Icon(Icons.link_off),
tooltip: "Disconnect",
)
],
),
body: Padding(
padding: const [Link](12),
child: Column(
crossAxisAlignment: [Link],
children: [
Card(
child: Padding(
padding: const [Link](12),
child: Column(
crossAxisAlignment: [Link],
children: [
Text("Status: $_status", style: const TextStyle(fontWeight: FontWeight.w600)),
const SizedBox(height: 6),
Text("Connected device: $connectedName"),
const SizedBox(height: 6),
Text("Last sent: $_lastSent"),
],
),
),
),

const SizedBox(height: 10),

Row(
children: [
Expanded(
child: [Link](
onPressed: _isScanning ? null : _startScan,
icon: const Icon([Link]),
label: Text(_isScanning ? "Scanning..." : "Scan BLE Devices"),
),
),
],
),

const SizedBox(height: 10),

Expanded(
child: _scanResults.isEmpty
? const Center(child: Text("No devices yet. Tap Scan."))
: [Link](
itemCount: _scanResults.length,
itemBuilder: (context, i) => _deviceTile(_scanResults[i]),
),
),

const SizedBox(height: 10),


Card(
child: Padding(
padding: const [Link](12),
child: Column(
children: [
const Text("Send Arrow Commands", style: TextStyle(fontWeight: [Link])
const SizedBox(height: 10),

Row(
children: [
_commandButton("Left", "LEFT", Icons.turn_left),
_commandButton("Right", "RIGHT", Icons.turn_right),
],
),
Row(
children: [
_commandButton("Straight", "STRAIGHT", [Link]),
_commandButton("U■Turn", "UTURN", Icons.u_turn_left),
],
),
Row(
children: [
_commandButton("Arrived", "ARRIVED", [Link]),
_commandButton("Speed 40", "SPEED:40", [Link]),
],
),
],
),
),
),
],
),
),
);
}
}
How to run it (quick steps)
1) Create project:
flutter create moto_puck_app
2) Replace [Link] dependencies (add flutter_blue_plus)
3) Run:
flutter pub get
4) Replace lib/[Link] with the code in this PDF
5) Android:
flutter run
6) iOS:
cd ios && pod install && cd ..
flutter run

Important notes (so it actually works)


• On Android 12+, Bluetooth permissions are strict. If scan shows nothing, your phone may be blocking
permissions.
• Use a real phone, not emulator.
• Your device firmware must expose a BLE service with a writable characteristic. This app automatically finds
the first writable characteristic (simple MVP).
• If you want a clean product later, we will lock to a specific Service UUID and Characteristic UUID.

Next upgrade (recommended)


If you want the app to do real navigation: we will integrate a routing engine and then send the turn
instructions to the puck. For India-only, we can use Google Maps directions API (paid) or OpenStreetMap
based routing.

You might also like