From 46a16ef9ec08610433fa0e74213cb6afa75bb00a Mon Sep 17 00:00:00 2001 From: "T. Coutlakis" Date: Thu, 30 Jul 2026 11:08:10 +0200 Subject: [PATCH 1/3] fix(network_info_plus): Fix subnet mask and broadcast on Linux, using network byte order --- .../lib/src/network_info_plus_linux.dart | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/network_info_plus/network_info_plus/lib/src/network_info_plus_linux.dart b/packages/network_info_plus/network_info_plus/lib/src/network_info_plus_linux.dart index 92f2314729..1ffe63743a 100644 --- a/packages/network_info_plus/network_info_plus/lib/src/network_info_plus_linux.dart +++ b/packages/network_info_plus/network_info_plus/lib/src/network_info_plus_linux.dart @@ -97,7 +97,7 @@ class NetworkInfoPlusLinuxPlugin extends NetworkInfoPlatform { String? _getSubnetMask(List>? data) { final prefix = data?.firstOrNull?['prefix'] as int; - final mask = 0xffffffff >> (32 - prefix); + final mask = (0xffffffff << (32 - prefix)) & 0xffffffff; return mask.toIpString(); } @@ -113,17 +113,26 @@ class NetworkInfoPlusLinuxPlugin extends NetworkInfoPlatform { } extension _IpInt on int { - int byteAt(int i) => (this >> (i * 8)) & 0xff; - String toIpString() => '${byteAt(0)}.${byteAt(1)}.${byteAt(2)}.${byteAt(3)}'; + /// Returns the i-th octet in network (big-endian) order. + /// i must be 0..3. + int octet(int i) { + assert(i >= 0 && i <= 3); + return (this >> (24 - i * 8)) & 0xff; + } + + String toIpString() { + final v = this & 0xFFFFFFFF; + return '${v.octet(0)}.${v.octet(1)}.${v.octet(2)}.${v.octet(3)}'; + } } extension _IpString on String { int toIpInt() { final parts = split('.'); - return parts.intAtOrZero(3) << 24 | - parts.intAtOrZero(2) << 16 | - parts.intAtOrZero(1) << 8 | - parts.intAtOrZero(0); + return parts.intAtOrZero(0) << 24 | + parts.intAtOrZero(1) << 16 | + parts.intAtOrZero(2) << 8 | + parts.intAtOrZero(3); } } From 91cc65dc4fe11b20e772f32a4cccf2e8c5ef08e4 Mon Sep 17 00:00:00 2001 From: "T. Coutlakis" Date: Thu, 30 Jul 2026 12:41:05 +0200 Subject: [PATCH 2/3] fix(network_info_plus): format --- .../network_info_plus/lib/src/network_info_plus_linux.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/network_info_plus/network_info_plus/lib/src/network_info_plus_linux.dart b/packages/network_info_plus/network_info_plus/lib/src/network_info_plus_linux.dart index 1ffe63743a..3442674a8b 100644 --- a/packages/network_info_plus/network_info_plus/lib/src/network_info_plus_linux.dart +++ b/packages/network_info_plus/network_info_plus/lib/src/network_info_plus_linux.dart @@ -119,7 +119,7 @@ extension _IpInt on int { assert(i >= 0 && i <= 3); return (this >> (24 - i * 8)) & 0xff; } - + String toIpString() { final v = this & 0xFFFFFFFF; return '${v.octet(0)}.${v.octet(1)}.${v.octet(2)}.${v.octet(3)}'; From 4e1dde48713f785225b4d9407b98a6a3bc739120 Mon Sep 17 00:00:00 2001 From: Volodymyr Date: Mon, 28 Sep 2026 14:30:16 +0300 Subject: [PATCH 3/3] test(network_info_plus): Add Linux subnet mask and broadcast tests Cover byte-aligned and non-byte-aligned prefixes (/0 to /32) using fakes for NetworkManagerClient, so tests no longer need to run on Linux. Co-Authored-By: Claude Opus 5.5 --- .../test/network_info_plus_linux_test.dart | 72 ++++++++++++++++++- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/packages/network_info_plus/network_info_plus/test/network_info_plus_linux_test.dart b/packages/network_info_plus/network_info_plus/test/network_info_plus_linux_test.dart index dc5ab1195a..98e2d3a145 100644 --- a/packages/network_info_plus/network_info_plus/test/network_info_plus_linux_test.dart +++ b/packages/network_info_plus/network_info_plus/test/network_info_plus_linux_test.dart @@ -1,13 +1,79 @@ -@TestOn('linux') -library; - import 'package:flutter_test/flutter_test.dart'; import 'package:network_info_plus/network_info_plus.dart'; import 'package:network_info_plus_platform_interface/network_info_plus_platform_interface.dart'; +import 'package:nm/nm.dart'; void main() { test('registered instance', () { NetworkInfoPlusLinuxPlugin.registerWith(); expect(NetworkInfoPlatform.instance, isA()); }); + + group('IPv4 subnet mask and broadcast', () { + // address, prefix, expected mask, expected broadcast + const cases = [ + ('192.168.1.10', 24, '255.255.255.0', '192.168.1.255'), + ('192.168.1.10', 22, '255.255.252.0', '192.168.3.255'), + ('192.168.1.10', 25, '255.255.255.128', '192.168.1.127'), + ('10.1.2.3', 8, '255.0.0.0', '10.255.255.255'), + ('172.16.5.4', 12, '255.240.0.0', '172.31.255.255'), + ('200.100.50.25', 20, '255.255.240.0', '200.100.63.255'), + ('10.0.0.1', 30, '255.255.255.252', '10.0.0.3'), + ('192.168.1.10', 32, '255.255.255.255', '192.168.1.10'), + ('192.168.1.10', 0, '0.0.0.0', '255.255.255.255'), + ]; + + for (final (address, prefix, mask, broadcast) in cases) { + test('$address/$prefix', () async { + final plugin = NetworkInfoPlusLinuxPlugin() + ..createClient = () => _FakeClient(address: address, prefix: prefix); + + expect(await plugin.getWifiSubmask(), mask); + expect(await plugin.getWifiBroadcast(), broadcast); + }); + } + }); + + test('wifi IP and gateway are passed through', () async { + final plugin = NetworkInfoPlusLinuxPlugin() + ..createClient = () => _FakeClient(address: '192.168.1.10', prefix: 22); + + expect(await plugin.getWifiIP(), '192.168.1.10'); + expect(await plugin.getWifiGatewayIP(), '192.168.0.1'); + }); +} + +class _FakeClient extends Fake implements NetworkManagerClient { + _FakeClient({required String address, required int prefix}) + : primaryConnection = _FakeConnection( + _FakeIp4Config([ + {'address': address, 'prefix': prefix}, + ]), + ); + + @override + final NetworkManagerActiveConnection? primaryConnection; + + @override + Future connect() async {} + + @override + Future close() async {} +} + +class _FakeConnection extends Fake implements NetworkManagerActiveConnection { + _FakeConnection(this.ip4Config); + + @override + final NetworkManagerIP4Config? ip4Config; +} + +class _FakeIp4Config extends Fake implements NetworkManagerIP4Config { + _FakeIp4Config(this.addressData); + + @override + final List> addressData; + + @override + String get gateway => '192.168.0.1'; }