123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- import 'dart:convert';
- 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);
- _InstallPageState() {
- 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,
- enableResizeEdges: windowManagerEnableResizeEdges,
- 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),
- );
- _InstallPageBodyState() {
- controller = TextEditingController(text: bind.installInstallPath());
- final installOptions = jsonDecode(bind.installInstallOptions());
- startmenu.value = installOptions['STARTMENUSHORTCUTS'] != '0';
- desktopicon.value = installOptions['DESKTOPSHORTCUTS'] != '0';
- }
- @override
- void initState() {
- windowManager.addListener(this);
- 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),
- ),
- ).workaroundFreezeLinuxMint().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());
- }
- }
- }
|