Signed-off-by: fufesou <shuanglongchen@yeah.net> Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
		
			
				
	
	
		
			262 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'package:file_picker/file_picker.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:flutter_hbb/common.dart';
 | 
						|
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
 | 
						|
import 'package:flutter_hbb/models/platform_model.dart';
 | 
						|
import 'package:flutter_hbb/models/state_model.dart';
 | 
						|
import 'package:get/get.dart';
 | 
						|
import 'package:path/path.dart';
 | 
						|
import 'package:url_launcher/url_launcher_string.dart';
 | 
						|
import 'package:window_manager/window_manager.dart';
 | 
						|
 | 
						|
class InstallPage extends StatefulWidget {
 | 
						|
  const InstallPage({Key? key}) : super(key: key);
 | 
						|
 | 
						|
  @override
 | 
						|
  State<InstallPage> createState() => _InstallPageState();
 | 
						|
}
 | 
						|
 | 
						|
class _InstallPageState extends State<InstallPage> {
 | 
						|
  final tabController = DesktopTabController(tabType: DesktopTabType.main);
 | 
						|
 | 
						|
  @override
 | 
						|
  void initState() {
 | 
						|
    super.initState();
 | 
						|
    Get.put<DesktopTabController>(tabController);
 | 
						|
    const label = "install";
 | 
						|
    tabController.add(TabInfo(
 | 
						|
        key: label,
 | 
						|
        label: label,
 | 
						|
        closable: false,
 | 
						|
        page: _InstallPageBody(
 | 
						|
          key: const ValueKey(label),
 | 
						|
        )));
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  void dispose() {
 | 
						|
    super.dispose();
 | 
						|
    Get.delete<DesktopTabController>();
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    return DragToResizeArea(
 | 
						|
      resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
 | 
						|
      child: Container(
 | 
						|
        child: Scaffold(
 | 
						|
            backgroundColor: Theme.of(context).colorScheme.background,
 | 
						|
            body: DesktopTab(controller: tabController)),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class _InstallPageBody extends StatefulWidget {
 | 
						|
  const _InstallPageBody({Key? key}) : super(key: key);
 | 
						|
 | 
						|
  @override
 | 
						|
  State<_InstallPageBody> createState() => _InstallPageBodyState();
 | 
						|
}
 | 
						|
 | 
						|
class _InstallPageBodyState extends State<_InstallPageBody>
 | 
						|
    with WindowListener {
 | 
						|
  late final TextEditingController controller;
 | 
						|
  final RxBool startmenu = true.obs;
 | 
						|
  final RxBool desktopicon = true.obs;
 | 
						|
  final RxBool showProgress = false.obs;
 | 
						|
  final RxBool btnEnabled = true.obs;
 | 
						|
 | 
						|
  // todo move to theme.
 | 
						|
  final buttonStyle = OutlinedButton.styleFrom(
 | 
						|
    textStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.normal),
 | 
						|
    padding: EdgeInsets.symmetric(vertical: 15, horizontal: 12),
 | 
						|
  );
 | 
						|
 | 
						|
  @override
 | 
						|
  void initState() {
 | 
						|
    windowManager.addListener(this);
 | 
						|
    controller = TextEditingController(text: bind.installInstallPath());
 | 
						|
    super.initState();
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  void dispose() {
 | 
						|
    windowManager.removeListener(this);
 | 
						|
    super.dispose();
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  void onWindowClose() {
 | 
						|
    gFFI.close();
 | 
						|
    super.onWindowClose();
 | 
						|
    windowManager.setPreventClose(false);
 | 
						|
    windowManager.close();
 | 
						|
  }
 | 
						|
 | 
						|
  InkWell Option(RxBool option, {String label = ''}) {
 | 
						|
    return InkWell(
 | 
						|
      // todo mouseCursor: "SystemMouseCursors.forbidden" or no cursor on btnEnabled == false
 | 
						|
      borderRadius: BorderRadius.circular(6),
 | 
						|
      onTap: () => btnEnabled.value ? option.value = !option.value : null,
 | 
						|
      child: Row(
 | 
						|
        children: [
 | 
						|
          Obx(
 | 
						|
            () => Checkbox(
 | 
						|
              visualDensity: VisualDensity(horizontal: -4, vertical: -4),
 | 
						|
              value: option.value,
 | 
						|
              onChanged: (v) =>
 | 
						|
                  btnEnabled.value ? option.value = !option.value : null,
 | 
						|
            ).marginOnly(right: 8),
 | 
						|
          ),
 | 
						|
          Expanded(
 | 
						|
            child: Text(translate(label)),
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    final double em = 13;
 | 
						|
    final isDarkTheme = MyTheme.currentThemeMode() == ThemeMode.dark;
 | 
						|
    return Scaffold(
 | 
						|
        backgroundColor: null,
 | 
						|
        body: SingleChildScrollView(
 | 
						|
          child: Column(
 | 
						|
            crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
            children: [
 | 
						|
              Text(translate('Installation'),
 | 
						|
                  style: Theme.of(context).textTheme.headlineMedium),
 | 
						|
              Row(
 | 
						|
                children: [
 | 
						|
                  Text('${translate('Installation Path')}:')
 | 
						|
                      .marginOnly(right: 10),
 | 
						|
                  Expanded(
 | 
						|
                    child: TextField(
 | 
						|
                      controller: controller,
 | 
						|
                      readOnly: true,
 | 
						|
                      decoration: InputDecoration(
 | 
						|
                        contentPadding: EdgeInsets.all(0.75 * em),
 | 
						|
                      ),
 | 
						|
                    ).marginOnly(right: 10),
 | 
						|
                  ),
 | 
						|
                  Obx(
 | 
						|
                    () => OutlinedButton.icon(
 | 
						|
                      icon: Icon(Icons.folder_outlined, size: 16),
 | 
						|
                      onPressed: btnEnabled.value ? selectInstallPath : null,
 | 
						|
                      style: buttonStyle,
 | 
						|
                      label: Text(translate('Change Path')),
 | 
						|
                    ),
 | 
						|
                  )
 | 
						|
                ],
 | 
						|
              ).marginSymmetric(vertical: 2 * em),
 | 
						|
              Option(startmenu, label: 'Create start menu shortcuts')
 | 
						|
                  .marginOnly(bottom: 7),
 | 
						|
              Option(desktopicon, label: 'Create desktop icon'),
 | 
						|
              Container(
 | 
						|
                  padding: EdgeInsets.all(12),
 | 
						|
                  decoration: BoxDecoration(
 | 
						|
                    color: isDarkTheme
 | 
						|
                        ? Color.fromARGB(135, 87, 87, 90)
 | 
						|
                        : Colors.grey[100],
 | 
						|
                    borderRadius: BorderRadius.circular(8),
 | 
						|
                    border: Border.all(color: Colors.grey),
 | 
						|
                  ),
 | 
						|
                  child: Row(
 | 
						|
                    children: [
 | 
						|
                      Icon(Icons.info_outline_rounded, size: 32)
 | 
						|
                          .marginOnly(right: 16),
 | 
						|
                      Column(
 | 
						|
                        crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
                        children: [
 | 
						|
                          Text(translate('agreement_tip'))
 | 
						|
                              .marginOnly(bottom: em),
 | 
						|
                          InkWell(
 | 
						|
                            hoverColor: Colors.transparent,
 | 
						|
                            onTap: () => launchUrlString(
 | 
						|
                                'https://rustdesk.com/privacy.html'),
 | 
						|
                            child: Tooltip(
 | 
						|
                              message: 'https://rustdesk.com/privacy.html',
 | 
						|
                              child: Row(children: [
 | 
						|
                                Icon(Icons.launch_outlined, size: 16)
 | 
						|
                                    .marginOnly(right: 5),
 | 
						|
                                Text(
 | 
						|
                                  translate('End-user license agreement'),
 | 
						|
                                  style: const TextStyle(
 | 
						|
                                      decoration: TextDecoration.underline),
 | 
						|
                                )
 | 
						|
                              ]),
 | 
						|
                            ),
 | 
						|
                          ),
 | 
						|
                        ],
 | 
						|
                      )
 | 
						|
                    ],
 | 
						|
                  )).marginSymmetric(vertical: 2 * em),
 | 
						|
              Row(
 | 
						|
                children: [
 | 
						|
                  Expanded(
 | 
						|
                    // NOT use Offstage to wrap LinearProgressIndicator
 | 
						|
                    child: Obx(() => showProgress.value
 | 
						|
                        ? LinearProgressIndicator().marginOnly(right: 10)
 | 
						|
                        : Offstage()),
 | 
						|
                  ),
 | 
						|
                  Obx(
 | 
						|
                    () => OutlinedButton.icon(
 | 
						|
                      icon: Icon(Icons.close_rounded, size: 16),
 | 
						|
                      label: Text(translate('Cancel')),
 | 
						|
                      onPressed:
 | 
						|
                          btnEnabled.value ? () => windowManager.close() : null,
 | 
						|
                      style: buttonStyle,
 | 
						|
                    ).marginOnly(right: 10),
 | 
						|
                  ),
 | 
						|
                  Obx(
 | 
						|
                    () => ElevatedButton.icon(
 | 
						|
                      icon: Icon(Icons.done_rounded, size: 16),
 | 
						|
                      label: Text(translate('Accept and Install')),
 | 
						|
                      onPressed: btnEnabled.value ? install : null,
 | 
						|
                      style: buttonStyle,
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                  Offstage(
 | 
						|
                    offstage: bind.installShowRunWithoutInstall(),
 | 
						|
                    child: Obx(
 | 
						|
                      () => OutlinedButton.icon(
 | 
						|
                        icon: Icon(Icons.screen_share_outlined, size: 16),
 | 
						|
                        label: Text(translate('Run without install')),
 | 
						|
                        onPressed: btnEnabled.value
 | 
						|
                            ? () => bind.installRunWithoutInstall()
 | 
						|
                            : null,
 | 
						|
                        style: buttonStyle,
 | 
						|
                      ).marginOnly(left: 10),
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                ],
 | 
						|
              )
 | 
						|
            ],
 | 
						|
          ).paddingSymmetric(horizontal: 4 * em, vertical: 3 * em),
 | 
						|
        ));
 | 
						|
  }
 | 
						|
 | 
						|
  void install() {
 | 
						|
    do_install() {
 | 
						|
      btnEnabled.value = false;
 | 
						|
      showProgress.value = true;
 | 
						|
      String args = '';
 | 
						|
      if (startmenu.value) args += ' startmenu';
 | 
						|
      if (desktopicon.value) args += ' desktopicon';
 | 
						|
      bind.installInstallMe(options: args, path: controller.text);
 | 
						|
    }
 | 
						|
    do_install();
 | 
						|
  }
 | 
						|
 | 
						|
  void selectInstallPath() async {
 | 
						|
    String? install_path = await FilePicker.platform
 | 
						|
        .getDirectoryPath(initialDirectory: controller.text);
 | 
						|
    if (install_path != null) {
 | 
						|
      controller.text = join(install_path, await bind.mainGetAppName());
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |