zeba_academy_multi_device_sync 1.0.0
zeba_academy_multi_device_sync: ^1.0.0 copied to clipboard
Offline-first multi device sync over LAN or Bluetooth for Flutter apps
example/lib/main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:zeba_academy_multi_device_sync/zeba_academy_multi_device_sync.dart';
void main() {
runApp(const SyncExampleApp());
}
class SyncExampleApp extends StatelessWidget {
const SyncExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Multi Device Sync Demo",
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.indigo,
),
home: const SyncHomePage(),
);
}
}
class SyncHomePage extends StatefulWidget {
const SyncHomePage({super.key});
@override
State<SyncHomePage> createState() => _SyncHomePageState();
}
class _SyncHomePageState extends State<SyncHomePage> {
final ZebaMultiDeviceSync sync = ZebaMultiDeviceSync();
final TextEditingController controller = TextEditingController();
final List<String> tasks = [];
final List<String> events = [];
StreamSubscription? eventSub;
bool started = false;
@override
void initState() {
super.initState();
eventSub = sync.events.stream.listen((event) {
setState(() {
events.insert(0, event);
});
});
}
@override
void dispose() {
controller.dispose();
eventSub?.cancel();
super.dispose();
}
Future<void> startSync() async {
if (started) return;
await sync.start();
setState(() {
started = true;
events.insert(0, "Sync started on device: ${sync.deviceId}");
});
}
void addTask() {
final text = controller.text.trim();
if (text.isEmpty) return;
sync.save("task_${tasks.length}", text);
setState(() {
tasks.add(text);
controller.clear();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
/// HEADER
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Colors.indigo, Colors.blueAccent],
),
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Zeba Academy",
style: TextStyle(
color: Colors.white70,
fontSize: 14,
),
),
SizedBox(height: 4),
Text(
"Multi Device Sync",
style: TextStyle(
color: Colors.white,
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
],
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
/// STATUS CARD
Card(
elevation: 2,
child: ListTile(
leading: Icon(
started ? Icons.cloud_done : Icons.cloud_off,
color: started ? Colors.green : Colors.red,
),
title: Text(
started ? "Sync Active" : "Sync Not Started",
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text("Device ID: ${sync.deviceId}"),
trailing: ElevatedButton.icon(
onPressed: startSync,
icon: const Icon(Icons.sync),
label: const Text("Start"),
),
),
),
const SizedBox(height: 20),
/// ADD TASK
Row(
children: [
Expanded(
child: TextField(
controller: controller,
decoration: InputDecoration(
hintText: "Add shared task",
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: addTask,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 18, vertical: 16),
),
child: const Icon(Icons.add),
)
],
),
const SizedBox(height: 20),
/// TASK LIST
const Align(
alignment: Alignment.centerLeft,
child: Text(
"Shared Tasks",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
const SizedBox(height: 8),
Expanded(
child: ListView.builder(
itemCount: tasks.length,
itemBuilder: (context, index) {
return Card(
elevation: 1,
child: ListTile(
leading: const Icon(
Icons.task_alt,
color: Colors.green,
),
title: Text(tasks[index]),
),
);
},
),
),
const Divider(),
/// EVENT LOGS
const Align(
alignment: Alignment.centerLeft,
child: Text(
"Sync Events",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
const SizedBox(height: 8),
SizedBox(
height: 120,
child: ListView.builder(
itemCount: events.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Text(
"• ${events[index]}",
style: const TextStyle(fontSize: 12),
),
);
},
),
),
],
),
),
),
],
),
),
);
}
}