refactor: depend on an Iperf3Runner interface instead of the service
The presentation layer imported Iperf3Service directly, so the UI was welded to the MethodChannel transport: the view model named it, and the screen constructed one. Dependency direction was already inward-only, but the UI depended on an implementation rather than a contract. Iperf3Runner states that contract in the domain layer in five members, in pure Dart with no Flutter import, and Iperf3Service implements it. The view model and screen now take an Iperf3Runner; main.dart is the only file that names a concrete one, which makes it the composition root. Two consequences worth having. The UI can be driven against an in-memory runner with no MethodChannel, native library or device involved. And a test double implements five stable members rather than tracking every public member the transport happens to expose — the previous double, forced to implement the concrete class, broke twice while this branch's work was going on. Also drops flutter/foundation from the data layer: debugPrint gives way to dart:developer, leaving flutter/services (where MethodChannel lives) as that layer's only Flutter dependency. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,24 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.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/domain/services/iperf3_runner.dart';
|
||||
import 'package:iperf3gui/ui/features/test_run/iperf3_event_formatter.dart';
|
||||
import 'package:iperf3gui/ui/features/test_run/test_run_view_model.dart';
|
||||
|
||||
/// Main test run screen.
|
||||
///
|
||||
/// iperf3 is compiled as a native shared library (libiperf3jni.so) and accessed
|
||||
/// via JNI, so there is no binary extraction or chmod step.
|
||||
/// Takes an [Iperf3Runner] rather than constructing one, so this layer names no
|
||||
/// transport: the Android app injects the JNI-backed implementation from
|
||||
/// main(), and a test injects a fake.
|
||||
class TestRunScreen extends StatelessWidget {
|
||||
const TestRunScreen({super.key});
|
||||
const TestRunScreen({required this.runner, super.key});
|
||||
|
||||
final Iperf3Runner runner;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) => TestRunViewModel(
|
||||
iperf3Service: Iperf3Service(),
|
||||
),
|
||||
create: (_) => TestRunViewModel(iperf3Service: runner),
|
||||
child: const _TestRunContent(),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user