k_card/k_phone/lib/main.dart

164 lines
4.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'proxy_service.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await ProxyService.initialize();
runApp(const KPhoneApp());
}
class KPhoneApp extends StatelessWidget {
const KPhoneApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'k_phone',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
useMaterial3: true,
),
home: const ProxyStatusScreen(),
);
}
}
class ProxyStatusScreen extends StatefulWidget {
const ProxyStatusScreen({super.key});
@override
State<ProxyStatusScreen> createState() => _ProxyStatusScreenState();
}
class _ProxyStatusScreenState extends State<ProxyStatusScreen> {
bool _serviceRunning = false;
bool _cardAttached = false;
String _statusMessage = 'Stopped';
final List<String> _log = [];
@override
void initState() {
super.initState();
_subscribeToService();
}
void _subscribeToService() {
final service = FlutterBackgroundService();
// Sync initial running state
service.isRunning().then((running) {
if (mounted) setState(() => _serviceRunning = running);
});
service.on('status').listen((event) {
if (event == null) return;
if (mounted) {
setState(() {
_serviceRunning = event['running'] as bool? ?? false;
_cardAttached = event['cardAttached'] as bool? ?? false;
_statusMessage = event['message'] as String? ?? '';
final log = event['log'] as String?;
if (log != null) {
_log.insert(0, log);
if (_log.length > 200) _log.removeLast();
}
});
}
});
}
Future<void> _toggleService() async {
final service = FlutterBackgroundService();
final running = await service.isRunning();
if (running) {
service.invoke('stop');
setState(() {
_serviceRunning = false;
_cardAttached = false;
_statusMessage = 'Stopped';
});
} else {
await service.startService();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('k_phone — ChromeCard proxy'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_StatusTile(
label: 'Proxy service',
ok: _serviceRunning,
value: _serviceRunning ? 'Running on :8771' : 'Stopped',
),
const SizedBox(height: 8),
_StatusTile(
label: 'ChromeCard (USB)',
ok: _cardAttached,
value: _cardAttached ? 'Attached' : 'Not detected',
),
const SizedBox(height: 8),
Text(
_statusMessage,
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 16),
FilledButton(
onPressed: _toggleService,
child: Text(_serviceRunning ? 'Stop proxy' : 'Start proxy'),
),
const Divider(height: 32),
const Text('Log', style: TextStyle(fontWeight: FontWeight.bold)),
Expanded(
child: ListView.builder(
itemCount: _log.length,
itemBuilder: (_, i) => Text(
_log[i],
style: const TextStyle(fontSize: 11, fontFamily: 'monospace'),
),
),
),
],
),
),
);
}
}
class _StatusTile extends StatelessWidget {
final String label;
final bool ok;
final String value;
const _StatusTile({
required this.label,
required this.ok,
required this.value,
});
@override
Widget build(BuildContext context) {
return Row(
children: [
Icon(
ok ? Icons.check_circle : Icons.radio_button_unchecked,
color: ok ? Colors.green : Colors.grey,
size: 18,
),
const SizedBox(width: 8),
Text('$label: ', style: const TextStyle(fontWeight: FontWeight.w600)),
Text(value),
],
);
}
}