import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:iperf3gui/data/services/iperf3_service.dart'; import 'package:iperf3gui/domain/models/test_configuration.dart'; import 'package:iperf3gui/ui/features/test_run/test_run_view_model.dart'; /// Main test run screen. /// /// iperf3 is now compiled as a native shared library (libiperf3jni.so) /// and accessed via JNI. No binary extraction or chmod needed. class TestRunScreen extends StatelessWidget { const TestRunScreen({super.key}); @override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (_) => TestRunViewModel( iperf3Service: Iperf3Service(), ), child: const _TestRunContent(), ); } } class _TestRunContent extends StatefulWidget { const _TestRunContent(); @override State<_TestRunContent> createState() => _TestRunContentState(); } class _TestRunContentState extends State<_TestRunContent> { final _serverController = TextEditingController(text: 'speedtest.uztelecom.uz'); final _portController = TextEditingController(text: '5201'); @override void dispose() { _serverController.dispose(); _portController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('iperf3-gui'), ), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Server input TextField( controller: _serverController, decoration: const InputDecoration( labelText: 'Server', hintText: 'e.g. iperf.es.net or 192.168.1.1', ), ), const SizedBox(height: 16), // Port input TextField( controller: _portController, decoration: const InputDecoration( labelText: 'Port', hintText: '5201', ), keyboardType: TextInputType.number, ), const SizedBox(height: 16), // Go button Consumer( builder: (context, vm, _) { return ElevatedButton( onPressed: vm.state == TestRunState.running ? null : () { final config = TestConfiguration( server: _serverController.text, port: int.tryParse(_portController.text) ?? 5201, ); vm.runTest(config); }, child: vm.state == TestRunState.running ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('GO'), ); }, ), const SizedBox(height: 16), // Status indicator Consumer( builder: (context, vm, _) { final statusText = switch (vm.state) { TestRunState.idle => '', TestRunState.running => 'Running...', TestRunState.completed => 'Test complete', TestRunState.error => 'Error', }; return Text( statusText, style: TextStyle( color: vm.state == TestRunState.error ? Colors.red : Colors.grey, ), ); }, ), const SizedBox(height: 16), // Raw output text box Expanded( child: Consumer( builder: (context, vm, _) { final output = vm.result?.rawOutput ?? ''; return TextField( controller: TextEditingController(text: output), decoration: const InputDecoration( labelText: 'Raw Output', border: OutlineInputBorder(), ), maxLines: null, readOnly: true, ); }, ), ), ], ), ), ); } }