Skip to content

Instantly share code, notes, and snippets.

@md-riaz
Created May 27, 2025 12:28
Show Gist options
  • Save md-riaz/d60afcb8e0fd370f14b180913955264e to your computer and use it in GitHub Desktop.
Save md-riaz/d60afcb8e0fd370f14b180913955264e to your computer and use it in GitHub Desktop.
active devices flutter ui
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Active Devices',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: ChangeNotifierProvider<DeviceData>(
create: (context) => DeviceData(),
builder: (context, child) => const DeviceListScreen(),
),
);
}
}
class DeviceData extends ChangeNotifier {
DeviceData()
: devices = [
Device(
extension: '101',
agent: 'SIP.js/0.15.6',
displayName: 'Nazrul',
lanIp: '103.125.255.115',
ip: '103.125.255.115',
port: '40614',
status: 'Registered WSS-NAT expsecs 654',
ping: '0.00',
),
Device(
extension: '102',
agent: 'SIP.js/0.15.7',
displayName: 'Sabbir',
lanIp: '103.125.255.116',
ip: '103.125.255.116',
port: '40615',
status: 'Registered WSS-NAT expsecs 655',
ping: '0.01',
),
Device(
extension: '103',
agent: 'SIP.js/0.15.8',
displayName: 'Rahim',
lanIp: '103.125.255.117',
ip: '103.125.255.117',
port: '40616',
status: 'Registered WSS-NAT expsecs 656',
ping: '0.02',
),
Device(
extension: '104',
agent: 'SIP.js/0.15.9',
displayName: 'Karim',
lanIp: '103.125.255.118',
ip: '103.125.255.118',
port: '40617',
status: 'Registered WSS-NAT expsecs 657',
ping: '0.03',
),
Device(
extension: '105',
agent: 'SIP.js/0.15.10',
displayName: 'Sakib',
lanIp: '103.125.255.119',
ip: '103.125.255.119',
port: '40618',
status: 'Registered WSS-NAT expsecs 658',
ping: '0.04',
)
],
selectedExtensions = {};
List<Device> devices;
Set<String> selectedExtensions;
void toggleExtensionSelection(String extension) {
if (selectedExtensions.contains(extension)) {
selectedExtensions.remove(extension);
} else {
selectedExtensions.add(extension);
}
notifyListeners();
}
void rebootDevice(String extension) {
// Simulate rebooting the device.
print('Rebooting device: $extension');
}
void unregisterDevice(String extension) {
// Simulate unregistering the device.
print('Unregistering device: $extension');
}
void rebootSelectedDevices() {
for (var extension in selectedExtensions) {
rebootDevice(extension);
}
selectedExtensions.clear();
notifyListeners();
}
void unregisterSelectedDevices() {
for (var extension in selectedExtensions) {
unregisterDevice(extension);
}
selectedExtensions.clear();
notifyListeners();
}
}
class Device {
Device({
required this.extension,
required this.agent,
required this.displayName,
required this.lanIp,
required this.ip,
required this.port,
required this.status,
required this.ping,
});
final String extension;
final String agent;
final String displayName;
final String lanIp;
final String ip;
final String port;
final String status;
final String ping;
}
class DeviceListScreen extends StatefulWidget {
const DeviceListScreen({super.key});
@override
State<DeviceListScreen> createState() => _DeviceListScreenState();
}
class _DeviceListScreenState extends State<DeviceListScreen> {
String _searchText = '';
bool _isSearchVisible = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurple[50],
title: _isSearchVisible
? TextField(
decoration: const InputDecoration(
hintText: 'Search...',
border: InputBorder.none,
),
onChanged: (text) {
setState(() {
_searchText = text;
});
},
)
: Text(
'Active Devices (${Provider.of<DeviceData>(context).devices.length})'),
actions: [
IconButton(
icon: Icon(_isSearchVisible ? Icons.cancel : Icons.search),
onPressed: () {
setState(() {
_isSearchVisible = !_isSearchVisible;
if (!_isSearchVisible) {
_searchText = '';
}
});
},
),
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () {
// Implement refresh functionality
},
),
PopupMenuButton<String>(
onSelected: (value) {
if (value == 'reboot') {
Provider.of<DeviceData>(context, listen: false).rebootSelectedDevices();
} else if (value == 'unregister') {
Provider.of<DeviceData>(context, listen: false).unregisterSelectedDevices();
}
},
itemBuilder: (BuildContext context) {
return [
const PopupMenuItem<String>(
value: 'reboot',
child: Text('Reboot Selected'),
),
const PopupMenuItem<String>(
value: 'unregister',
child: Text('Unregister Selected'),
),
];
},
),
],
),
body: Consumer<DeviceData>(
builder: (context, deviceData, child) {
final filteredDevices = deviceData.devices
.where((device) =>
device.extension.toLowerCase().contains(_searchText.toLowerCase()) ||
device.displayName.toLowerCase().contains(_searchText.toLowerCase()))
.toList();
return ListView.builder(
itemCount: filteredDevices.length,
itemBuilder: (context, index) {
final device = filteredDevices[index];
return DeviceCard(device: device);
},
);
},
),
);
}
}
class DeviceCard extends StatefulWidget {
const DeviceCard({super.key, required this.device});
final Device device;
@override
State<DeviceCard> createState() => _DeviceCardState();
}
class _DeviceCardState extends State<DeviceCard> {
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(8.0),
child: ExpansionTile(
tilePadding: EdgeInsets.zero,
leading: Consumer<DeviceData>(
builder: (context, deviceData, child) {
return Checkbox(
value: deviceData.selectedExtensions.contains(widget.device.extension),
onChanged: (bool? value) {
if (value != null) {
deviceData.toggleExtensionSelection(widget.device.extension);
}
},
);
},
),
title: Text('Extension: ${widget.device.extension}'),
subtitle: Text(widget.device.displayName),
children: [
Padding(
padding: const EdgeInsets.all(10.0),
child: DeviceInfoTable(device: widget.device),
),
],
),
);
}
}
class DeviceInfoTable extends StatelessWidget {
const DeviceInfoTable({super.key, required this.device});
final Device device;
@override
Widget build(BuildContext context) {
return Table(
columnWidths: const {
0: IntrinsicColumnWidth(),
1: FlexColumnWidth(),
},
border: TableBorder.all(color: Colors.grey[300]!),
children: [
_buildTableRow('Agent', device.agent),
_buildTableRow('LAN IP', device.lanIp),
_buildTableRow('IP', device.ip),
_buildTableRow('Port', device.port),
_buildTableRow('Status', device.status),
_buildTableRow('Ping', device.ping),
],
);
}
TableRow _buildTableRow(String label, String value) {
return TableRow(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'$label:',
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(value),
),
],
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment