Created
May 27, 2025 12:28
-
-
Save md-riaz/d60afcb8e0fd370f14b180913955264e to your computer and use it in GitHub Desktop.
active devices flutter ui
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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