mirror_connect 0.0.1
mirror_connect: ^0.0.1 copied to clipboard
A lightweight Flutter SDK that connects your app to the Magic Mirror real-time infrastructure
example/lib/main.dart
import 'dart:convert';
import 'dart:math' as math;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mirror_connect/mirror_connect.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:pretty_qr_code/pretty_qr_code.dart';
/// The main entry point of the application.
void main() {
WidgetsFlutterBinding.ensureInitialized();
// Set up event callbacks
runApp(const MyApp());
}
/// The root widget of the application.
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Socket Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Socket Demo Product Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String sessionId = "";
bool isConnected = false;
List<String> productId = [];
TextEditingController controller = TextEditingController();
@override
void initState() {
// generates a unique session ID, and then establishes a connection to the server.
super.initState();
sessionId = getSessionId();
connectToServer();
}
/// Generates a unique session ID based on the current timestamp and a random number.
String getSessionId() {
final random = math.Random();
final timestamp = DateTime.now().millisecondsSinceEpoch;
final randomNumber = random.nextInt(999999);
String currentId = '${timestamp}_$randomNumber';
return currentId;
}
/// Initializes and connects to the WebSocket server using the MagicConnect.
/// It sets up listeners for connection, incoming messages, and errors.
void connectToServer() {
// Initialize the SDK
MagicConnect.init();
MagicConnect.onConnect = () {
print("✅ Connected to Magic Mirror Server");
MagicConnect.send("joinSession", sessionId);
};
MagicConnect.onMessage = (data) {
if (data is Map && data.containsKey("scannedItem")) {
//for device is scanned or not
/*
IF data["scannedItem"] == sessionId is true so magic mirror connected with your session id so you can close or Hide the qr dialog
*/
if (data["scannedItem"] == sessionId) {
if (mounted) {
Navigator.pop(context); // Close the QR dialog
setState(() {
isConnected = true;
});
}
}
//device is disconnected
else if (data["scannedItem"] == "disconnect") {
setState(() {
isConnected = false;
});
}
}
};
MagicConnect.onError = (error) {
print("⚠️ Error: $error");
};
}
/// Sends a disconnect message to the server and updates the UI state.
void disconnectConnection() {
var disconnect = {
"sessionId": sessionId,
"data": {
"scannedItem": "disconnect",
"itemType": "try",
"timestamp": DateTime.now().toIso8601String(),
}
};
MagicConnect.send("scannerData", disconnect);
MagicConnect.onDisconnect;
setState(() {
isConnected = false;
productId.clear();
});
}
/// Sends product data to the server for the current session.
sentData(String product){
var productData = {
"sessionId": sessionId,
"data": {
"scannedItem": product,
"itemType": "product",
"timestamp": DateTime.now().toIso8601String(),
}
};
MagicConnect.send("scannerData", productData);
}
/// Builds and returns a widget that displays a QR code for the current session.
/// The QR code contains session and user details for the magic mirror to scan.
Widget qrWidget() {
Map<String, String> qrData = {
"urlPrefix": "xx_xx",//add url prefix provide by plushvie
"customerId": "xx_xx_xx",// add customer id provide by plushvie
"userName": "Avishkar", //add customer name
"userNumber": "9876543210", //add customer number
"employeeId":"12",//add JC's name or employee Id
"branch": "branch name",//Add store branch name here
"city": "City name",//Add store city name
"sessionId": sessionId,//here use you session id
"counterNumber": "1" // number of counter to be used,
};
return Material(
// color: Colors.black.withOpacity(0.8),
child: Stack(
children: [
GestureDetector(
onTap: () => Navigator.pop(context),
child: Container(color: Colors.transparent),
),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"Scan This QR",
style: TextStyle(
color: Colors.white,
fontSize: 25,
),
),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12)
),
child: PrettyQrView.data(
data: jsonEncode(qrData),
decoration: const PrettyQrDecoration(
shape: PrettyQrSmoothSymbol(
color: Colors.black,
),
),
),
),
const SizedBox(height: 24),
SizedBox(
height: 40,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0),
child: Image.network(
"https://plushvie.in/sdk_app/poweredbyplushvie.png"),
),
),
],
),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
actions: [
if (isConnected)
TextButton(
onPressed: disconnectConnection,
child: const Text("Disconnect"),
)
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.only(left: 16),
child: Row(
children: [
Expanded(
child: TextFormField(
controller: controller,
decoration: const InputDecoration(
border: InputBorder.none,
hintText: "Enter or scan a product code",
),
onFieldSubmitted: (value) {
if (value.isNotEmpty) {
sentData(value);
// sentProductData(value);
if (!productId.contains(value)) {
setState(() {
productId.add(value);
});
}
controller.clear();
}
},
),
),
IconButton(
onPressed: () async {
final scannerData = await Navigator.push<String>(
context,
MaterialPageRoute(
builder: (context) => const ScannerPage()),
);
if (scannerData != null && scannerData.isNotEmpty) {
setState(() {
controller.text = scannerData;
});
}
},
icon: const Icon(Icons.camera_alt_outlined),
tooltip: "Scan Barcode",
),
IconButton(
icon: const Icon(CupertinoIcons.arrow_right_circle_fill),
onPressed: () {
if (controller.text.isEmpty) return;
sentData(controller.text);
// sentProductData(controller.text);
if (!productId.contains(controller.text)) {
setState(() {
productId.add(controller.text);
});
}
controller.clear();
},
tooltip: "Send Product",
),
],
),
),
),
const SizedBox(height: 20),
Expanded(
child: productId.isEmpty
? const Center(child: Text("No products sent yet."))
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("Sent Products:", style: TextStyle(fontWeight: FontWeight.bold)),
Expanded(
child: ListView(
children: [
Wrap(
spacing: 8.0,
runSpacing: 4.0,
children:
List.generate(productId.length, (int index) {
return GestureDetector(
onTap: () {
setState(() {
controller.text = productId[index];
});
},
child: Chip(
label: Text(productId[index]),
onDeleted: () {
setState(() {
productId.removeAt(index);
});
},
),
);
}),
),
],
),
),
],
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => qrWidget(),
);
},
tooltip: 'Show Session QR',
child: const Icon(Icons.qr_code),
),
);
}
@override
/// Disposes of the TextEditingController when the widget is removed from the widget tree.
void dispose() {
controller.dispose();
super.dispose();
}
}
class ScannerPage extends StatefulWidget {
const ScannerPage({super.key});
@override
State<ScannerPage> createState() => _ScannerPageState();
}
class _ScannerPageState extends State<ScannerPage> {
final MobileScannerController barcodeController = MobileScannerController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Scan Barcode')),
body: MobileScanner(
controller: barcodeController,
onDetect: (BarcodeCapture capture) {
final barcode = capture.barcodes.first;
final code = barcode.rawValue;
if (code != null) {
barcodeController.stop();
Navigator.pop(context, code);
}
},
),
);
}
@override
/// Disposes of the MobileScannerController when the widget is removed from the widget tree.
void dispose() {
barcodeController.dispose();
super.dispose();
}
}
/*needed packages
------------------------------------------------------
For generate qr cod you can use any other package
flutter pub add pretty_qr_code */