123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- import 'dart:convert';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:flutter_hbb/common.dart';
- import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
- import 'package:flutter_hbb/models/model.dart';
- import 'package:flutter_hbb/models/platform_model.dart';
- import 'package:get/get.dart';
- const double _kColumn1Width = 30;
- const double _kColumn4Width = 100;
- const double _kRowHeight = 60;
- const double _kTextLeftMargin = 20;
- class _PortForward {
- int localPort;
- String remoteHost;
- int remotePort;
- _PortForward.fromJson(List<dynamic> json)
- : localPort = json[0] as int,
- remoteHost = json[1] as String,
- remotePort = json[2] as int;
- }
- class PortForwardPage extends StatefulWidget {
- const PortForwardPage({
- Key? key,
- required this.id,
- required this.password,
- required this.tabController,
- required this.isRDP,
- required this.isSharedPassword,
- this.forceRelay,
- this.connToken,
- }) : super(key: key);
- final String id;
- final String? password;
- final DesktopTabController tabController;
- final bool isRDP;
- final bool? forceRelay;
- final bool? isSharedPassword;
- final String? connToken;
- @override
- State<PortForwardPage> createState() => _PortForwardPageState();
- }
- class _PortForwardPageState extends State<PortForwardPage>
- with AutomaticKeepAliveClientMixin {
- final TextEditingController localPortController = TextEditingController();
- final TextEditingController remoteHostController = TextEditingController();
- final TextEditingController remotePortController = TextEditingController();
- RxList<_PortForward> pfs = RxList.empty(growable: true);
- late FFI _ffi;
- @override
- void initState() {
- super.initState();
- _ffi = FFI(null);
- _ffi.start(widget.id,
- isPortForward: true,
- password: widget.password,
- isSharedPassword: widget.isSharedPassword,
- forceRelay: widget.forceRelay,
- connToken: widget.connToken,
- isRdp: widget.isRDP);
- Get.put<FFI>(_ffi, tag: 'pf_${widget.id}');
- debugPrint("Port forward page init success with id ${widget.id}");
- // Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState.
- WidgetsBinding.instance.addPostFrameCallback((_) {
- widget.tabController.onSelected?.call(widget.id);
- });
- }
- @override
- void dispose() {
- _ffi.close();
- _ffi.dialogManager.dismissAll();
- Get.delete<FFI>(tag: 'pf_${widget.id}');
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- super.build(context);
- return Scaffold(
- backgroundColor: Theme.of(context).scaffoldBackgroundColor,
- body: FutureBuilder(future: () async {
- if (!widget.isRDP) {
- refreshTunnelConfig();
- }
- }(), builder: (context, snapshot) {
- if (snapshot.connectionState == ConnectionState.done) {
- return Container(
- decoration: BoxDecoration(
- border: Border.all(
- width: 20,
- color: Theme.of(context).scaffoldBackgroundColor)),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- children: [
- buildPrompt(context),
- Flexible(
- child: Container(
- decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.background,
- border: Border.all(width: 1, color: MyTheme.border)),
- child:
- widget.isRDP ? buildRdp(context) : buildTunnel(context),
- ),
- ),
- ],
- ),
- );
- }
- return const Offstage();
- }),
- );
- }
- buildPrompt(BuildContext context) {
- return Obx(() => Offstage(
- offstage: pfs.isEmpty && !widget.isRDP,
- child: Container(
- height: 45,
- color: const Color(0xFF007F00),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Text(
- translate('Listening ...'),
- style: const TextStyle(fontSize: 16, color: Colors.white),
- ),
- Text(
- translate('not_close_tcp_tip'),
- style: const TextStyle(
- fontSize: 10, color: Color(0xFFDDDDDD), height: 1.2),
- )
- ])).marginOnly(bottom: 8),
- ));
- }
- buildTunnel(BuildContext context) {
- text(String label) => Expanded(
- child: Text(translate(label)).marginOnly(left: _kTextLeftMargin));
- return Theme(
- data: Theme.of(context).copyWith(
- colorScheme: Theme.of(context).colorScheme,
- ),
- child: Obx(() => ListView.builder(
- controller: ScrollController(),
- itemCount: pfs.length + 2,
- itemBuilder: ((context, index) {
- if (index == 0) {
- return Container(
- height: 25,
- color: Theme.of(context).scaffoldBackgroundColor,
- child: Row(children: [
- text('Local Port'),
- const SizedBox(width: _kColumn1Width),
- text('Remote Host'),
- text('Remote Port'),
- SizedBox(
- width: _kColumn4Width, child: Text(translate('Action')))
- ]),
- );
- } else if (index == 1) {
- return buildTunnelAddRow(context);
- } else {
- return buildTunnelDataRow(context, pfs[index - 2], index - 2);
- }
- }))),
- );
- }
- buildTunnelAddRow(BuildContext context) {
- var portInputFormatter = [
- FilteringTextInputFormatter.allow(RegExp(
- r'^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$'))
- ];
- return Container(
- height: _kRowHeight,
- decoration:
- BoxDecoration(color: Theme.of(context).colorScheme.background),
- child: Row(children: [
- buildTunnelInputCell(context,
- controller: localPortController,
- inputFormatters: portInputFormatter),
- const SizedBox(
- width: _kColumn1Width, child: Icon(Icons.arrow_forward_sharp)),
- buildTunnelInputCell(context,
- controller: remoteHostController, hint: 'localhost'),
- buildTunnelInputCell(context,
- controller: remotePortController,
- inputFormatters: portInputFormatter),
- ElevatedButton(
- onPressed: () async {
- int? localPort = int.tryParse(localPortController.text);
- int? remotePort = int.tryParse(remotePortController.text);
- if (localPort != null &&
- remotePort != null &&
- (remoteHostController.text.isEmpty ||
- remoteHostController.text.trim().isNotEmpty)) {
- await bind.sessionAddPortForward(
- sessionId: _ffi.sessionId,
- localPort: localPort,
- remoteHost: remoteHostController.text.trim().isEmpty
- ? 'localhost'
- : remoteHostController.text.trim(),
- remotePort: remotePort);
- localPortController.clear();
- remoteHostController.clear();
- remotePortController.clear();
- refreshTunnelConfig();
- }
- },
- child: Text(
- translate('Add'),
- ),
- ).marginSymmetric(horizontal: 10),
- ]),
- );
- }
- buildTunnelInputCell(BuildContext context,
- {required TextEditingController controller,
- List<TextInputFormatter>? inputFormatters,
- String? hint}) {
- return Expanded(
- child: Padding(
- padding: const EdgeInsets.all(10.0),
- child: TextField(
- controller: controller,
- inputFormatters: inputFormatters,
- decoration: InputDecoration(
- hintText: hint,
- ))),
- );
- }
- Widget buildTunnelDataRow(BuildContext context, _PortForward pf, int index) {
- text(String label) => Expanded(
- child: Text(label, style: const TextStyle(fontSize: 20))
- .marginOnly(left: _kTextLeftMargin));
- return Container(
- height: _kRowHeight,
- decoration: BoxDecoration(
- color: index % 2 == 0
- ? MyTheme.currentThemeMode() == ThemeMode.dark
- ? const Color(0xFF202020)
- : const Color(0xFFF4F5F6)
- : Theme.of(context).colorScheme.background),
- child: Row(children: [
- text(pf.localPort.toString()),
- const SizedBox(width: _kColumn1Width),
- text(pf.remoteHost),
- text(pf.remotePort.toString()),
- SizedBox(
- width: _kColumn4Width,
- child: IconButton(
- icon: const Icon(Icons.close),
- onPressed: () async {
- await bind.sessionRemovePortForward(
- sessionId: _ffi.sessionId, localPort: pf.localPort);
- refreshTunnelConfig();
- },
- ),
- ),
- ]),
- );
- }
- void refreshTunnelConfig() async {
- String peer = bind.mainGetPeerSync(id: widget.id);
- Map<String, dynamic> config = jsonDecode(peer);
- List<dynamic> infos = config['port_forwards'] as List;
- List<_PortForward> result = List.empty(growable: true);
- for (var e in infos) {
- result.add(_PortForward.fromJson(e));
- }
- pfs.value = result;
- }
- buildRdp(BuildContext context) {
- text1(String label) => Expanded(
- child: Text(translate(label)).marginOnly(left: _kTextLeftMargin));
- text2(String label) => Expanded(
- child: Text(
- label,
- style: const TextStyle(fontSize: 20),
- ).marginOnly(left: _kTextLeftMargin));
- return Theme(
- data: Theme.of(context)
- .copyWith(colorScheme: Theme.of(context).colorScheme),
- child: ListView.builder(
- controller: ScrollController(),
- itemCount: 2,
- itemBuilder: ((context, index) {
- if (index == 0) {
- return Container(
- height: 25,
- color: Theme.of(context).scaffoldBackgroundColor,
- child: Row(children: [
- text1('Local Port'),
- const SizedBox(width: _kColumn1Width),
- text1('Remote Host'),
- text1('Remote Port'),
- ]),
- );
- } else {
- return Container(
- height: _kRowHeight,
- decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.background),
- child: Row(children: [
- Expanded(
- child: Align(
- alignment: Alignment.centerLeft,
- child: SizedBox(
- width: 120,
- child: ElevatedButton(
- onPressed: () =>
- bind.sessionNewRdp(sessionId: _ffi.sessionId),
- child: Text(
- translate('New RDP'),
- ),
- ).marginSymmetric(vertical: 10),
- ).marginOnly(left: 20),
- ),
- ),
- const SizedBox(
- width: _kColumn1Width,
- child: Icon(Icons.arrow_forward_sharp)),
- text2('localhost'),
- text2('RDP'),
- ]),
- );
- }
- })),
- );
- }
- @override
- bool get wantKeepAlive => true;
- }
|