Released 9.2.7

This commit is contained in:
Alessandro Autiero
2024-09-14 12:37:56 +02:00
parent a9af28273a
commit bfe15e43d9
15 changed files with 331 additions and 255 deletions

View File

@@ -133,9 +133,9 @@ Future<void> _downloadArchive(FortniteBuildDownloadOptions options, Completer st
throw _genericError; throw _genericError;
}, },
headers: byteStart == null || byteStart <= 0 ? { headers: byteStart == null || byteStart <= 0 ? {
"Cookie": "_c_t_c=1"
} : { } : {
"Cookie": "_c_t_c=1",
"Range": "bytes=${byteStart}-" "Range": "bytes=${byteStart}-"
}, },
) )

View File

@@ -7,7 +7,7 @@ environment:
sdk: ">=3.0.0 <=4.0.0" sdk: ">=3.0.0 <=4.0.0"
dependencies: dependencies:
dio: ^5.3.2 dio: ^5.7.0
win32: 3.0.0 win32: 3.0.0
ffi: ^2.1.0 ffi: ^2.1.0
path: ^1.8.3 path: ^1.8.3

View File

@@ -368,5 +368,7 @@
"automaticGameServerDialogStart": "Start server", "automaticGameServerDialogStart": "Start server",
"gameResetDefaultsName": "Reset", "gameResetDefaultsName": "Reset",
"gameResetDefaultsDescription": "Resets the game's settings to their default values", "gameResetDefaultsDescription": "Resets the game's settings to their default values",
"gameResetDefaultsContent": "Reset" "gameResetDefaultsContent": "Reset",
"selectFile": "Select a file",
"reset": "Reset"
} }

View File

@@ -109,7 +109,9 @@ class DllController extends GetxController {
duration: null duration: null
); );
} }
timestamp.value = await downloadRebootDll(url.text); final result = downloadRebootDll(url.text);
timestamp.value = await Future.wait([result, Future.delayed(const Duration(seconds: 1))], eagerError: false)
.then((_) => result);
status.value = UpdateStatus.success; status.value = UpdateStatus.success;
infoBarEntry?.close(); infoBarEntry?.close();
if(!silent) { if(!silent) {
@@ -126,15 +128,18 @@ class DllController extends GetxController {
error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error; error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error;
error = error.toLowerCase(); error = error.toLowerCase();
status.value = UpdateStatus.error; status.value = UpdateStatus.error;
showRebootInfoBar( infoBarEntry = showRebootInfoBar(
translations.downloadDllError("reboot.dll", error.toString()), translations.downloadDllError(error.toString(), "reboot.dll"),
duration: infoBarLongDuration, duration: infoBarLongDuration,
severity: InfoBarSeverity.error, severity: InfoBarSeverity.error,
action: Button( action: Button(
onPressed: () => updateGameServerDll( onPressed: () async {
force: true, infoBarEntry?.close();
silent: silent updateGameServerDll(
), force: true,
silent: silent
);
},
child: Text(translations.downloadDllRetry), child: Text(translations.downloadDllRetry),
) )
); );
@@ -215,7 +220,7 @@ class DllController extends GetxController {
error = error.toLowerCase(); error = error.toLowerCase();
final completer = Completer(); final completer = Completer();
await showRebootInfoBar( await showRebootInfoBar(
translations.downloadDllError(fileName, error.toString()), translations.downloadDllError(error.toString(), fileName),
duration: infoBarLongDuration, duration: infoBarLongDuration,
severity: InfoBarSeverity.error, severity: InfoBarSeverity.error,
onDismissed: () => completer.complete(null), onDismissed: () => completer.complete(null),

View File

@@ -18,9 +18,12 @@ void onError(Object exception, StackTrace? stackTrace, bool framework) {
} }
lastError = exception.toString(); lastError = exception.toString();
final route = ModalRoute.of(pageKey.currentContext!); if(inDialog){
if(route != null && !route.isCurrent){ final context = pageKey.currentContext;
Navigator.of(pageKey.currentContext!).pop(false); if(context != null) {
Navigator.of(context).pop(false);
inDialog = false;
}
} }
WidgetsBinding.instance.addPostFrameCallback((timeStamp) => showRebootDialog( WidgetsBinding.instance.addPostFrameCallback((timeStamp) => showRebootDialog(

View File

@@ -5,10 +5,13 @@ import 'dart:isolate';
import 'package:fluent_ui/fluent_ui.dart'; import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:reboot_common/common.dart'; import 'package:reboot_common/common.dart';
import 'package:reboot_launcher/src/controller/game_controller.dart'; import 'package:reboot_launcher/src/controller/game_controller.dart';
import 'package:reboot_launcher/src/messenger/abstract/dialog.dart'; import 'package:reboot_launcher/src/messenger/abstract/dialog.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/util/translations.dart'; import 'package:reboot_launcher/src/util/translations.dart';
import 'package:reboot_launcher/src/util/types.dart';
import 'package:reboot_launcher/src/widget/file_selector.dart'; import 'package:reboot_launcher/src/widget/file_selector.dart';
import 'package:universal_disk_space/universal_disk_space.dart'; import 'package:universal_disk_space/universal_disk_space.dart';
import 'package:windows_taskbar/windows_taskbar.dart'; import 'package:windows_taskbar/windows_taskbar.dart';
@@ -41,6 +44,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
SendPort? _downloadPort; SendPort? _downloadPort;
Object? _error; Object? _error;
StackTrace? _stackTrace; StackTrace? _stackTrace;
bool _selecting = false;
@override @override
void initState() { void initState() {
@@ -102,7 +106,12 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
return ErrorDialog( return ErrorDialog(
exception: _error ?? Exception(translations.unknownError), exception: _error ?? Exception(translations.unknownError),
stackTrace: _stackTrace, stackTrace: _stackTrace,
errorMessageBuilder: (exception) => translations.downloadVersionError(exception.toString()) errorMessageBuilder: (exception) {
var error = exception.toString();
error = error.after("Error: ")?.replaceAll(":", ",") ?? error.after(": ") ?? error;
error = error.toLowerCase();
return translations.downloadVersionError(error);
}
); );
case _DownloadStatus.done: case _DownloadStatus.done:
return InfoDialog( return InfoDialog(
@@ -274,7 +283,8 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
); );
} }
Widget _buildFormBody(List<FortniteBuild> builds) => Column( Widget _buildFormBody(List<FortniteBuild> builds) {
return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -292,7 +302,8 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
windowTitle: _source.value == _BuildSource.local ? translations.gameFolderPlaceWindowTitle : translations.buildInstallationDirectoryWindowTitle, windowTitle: _source.value == _BuildSource.local ? translations.gameFolderPlaceWindowTitle : translations.buildInstallationDirectoryWindowTitle,
controller: _pathController, controller: _pathController,
validator: _source.value == _BuildSource.local ? _checkGameFolder : _checkDownloadDestination, validator: _source.value == _BuildSource.local ? _checkGameFolder : _checkDownloadDestination,
folder: true folder: true,
allowNavigator: true
), ),
const SizedBox( const SizedBox(
@@ -300,6 +311,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
) )
], ],
); );
}
String? _checkGameFolder(text) { String? _checkGameFolder(text) {
if (text == null || text.isEmpty) { if (text == null || text.isEmpty) {

View File

@@ -153,9 +153,9 @@ class _BackendPageState extends RebootPageState<BackendPage> {
contentWidth: null, contentWidth: null,
content: Row( content: Row(
children: [ children: [
Text( Obx(() => Text(
_backendController.detached.value ? translations.on : translations.off _backendController.detached.value ? translations.on : translations.off
), )),
const SizedBox( const SizedBox(
width: 16.0 width: 16.0
), ),

View File

@@ -298,48 +298,46 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
}); });
} }
Widget _buildBody() { Widget _buildBody() => Expanded(
return Expanded( child: Padding(
child: Padding( padding: EdgeInsets.only(
padding: EdgeInsets.only( left: HomePage.kDefaultPadding,
left: HomePage.kDefaultPadding, right: HomePage.kDefaultPadding * 2,
right: HomePage.kDefaultPadding * 2, top: HomePage.kDefaultPadding,
top: HomePage.kDefaultPadding, bottom: HomePage.kDefaultPadding * 2
bottom: HomePage.kDefaultPadding * 2 ),
), child: Column(
child: Column( children: [
children: [ Expanded(
Expanded( child: ConstrainedBox(
child: ConstrainedBox( constraints: BoxConstraints(
constraints: BoxConstraints( maxWidth: 1000
maxWidth: 1000 ),
), child: Center(
child: Center( child: Column(
child: Column( children: [
children: [ _buildBodyHeader(),
_buildBodyHeader(), const SizedBox(height: 24.0),
const SizedBox(height: 24.0), Expanded(
Expanded( child: Stack(
child: Stack( fit: StackFit.loose,
fit: StackFit.loose, children: [
children: [ _buildBodyContent(),
_buildBodyContent(), InfoBarArea(
InfoBarArea( key: infoBarAreaKey
key: infoBarAreaKey )
) ],
], )
) ),
), ],
],
),
), ),
), ),
) ),
], )
) ],
), )
); ),
} );
Widget _buildBodyContent() => PageView.builder( Widget _buildBodyContent() => PageView.builder(
controller: _pageController, controller: _pageController,

View File

@@ -82,7 +82,7 @@ class _HostingPageState extends RebootPageState<HostPage> {
List<Widget> get settings => [ List<Widget> get settings => [
_information, _information,
buildVersionSelector( buildVersionSelector(
key: hostVersionOverlayTargetKey key: hostVersionOverlayTargetKey
), ),
_options, _options,
_internalFiles, _internalFiles,
@@ -175,9 +175,9 @@ class _HostingPageState extends RebootPageState<HostPage> {
contentWidth: null, contentWidth: null,
content: Obx(() => Row( content: Obx(() => Row(
children: [ children: [
Text( Obx(() => Text(
_hostingController.discoverable.value ? translations.on : translations.off _hostingController.discoverable.value ? translations.on : translations.off
), )),
const SizedBox( const SizedBox(
width: 16.0 width: 16.0
), ),
@@ -226,7 +226,7 @@ class _HostingPageState extends RebootPageState<HostPage> {
text: Text(entry.translatedName), text: Text(entry.translatedName),
onPressed: () => _hostingController.type.value = entry onPressed: () => _hostingController.type.value = entry
)).toList(), )).toList(),
disabled: _settingsController.debug.value disabled: _settingsController.debug.value
)), )),
), ),
SettingTile( SettingTile(
@@ -238,9 +238,9 @@ class _HostingPageState extends RebootPageState<HostPage> {
contentWidth: null, contentWidth: null,
content: Row( content: Row(
children: [ children: [
Text( Obx(() => Text(
_hostingController.autoRestart.value ? translations.on : translations.off _hostingController.autoRestart.value ? translations.on : translations.off
), )),
const SizedBox( const SizedBox(
width: 16.0 width: 16.0
), ),
@@ -278,151 +278,143 @@ class _HostingPageState extends RebootPageState<HostPage> {
title: Text(translations.settingsServerName), title: Text(translations.settingsServerName),
subtitle: Text(translations.settingsServerSubtitle), subtitle: Text(translations.settingsServerSubtitle),
children: [ children: [
SettingTile( _internalFilesServerType,
icon: Icon( _internalFilesUpdateTimer,
FluentIcons.timer_24_regular _internalFilesServerSource
), ],
title: Text(translations.settingsServerTypeName), );
subtitle: Text(translations.settingsServerTypeDescription),
content: Obx(() => DropDownButton(
onOpen: () => inDialog = true,
onClose: () => inDialog = false,
leading: Text(_dllController.customGameServer.value ? translations.settingsServerTypeCustomName : translations.settingsServerTypeEmbeddedName),
items: {
false: translations.settingsServerTypeEmbeddedName,
true: translations.settingsServerTypeCustomName
}.entries.map((entry) => MenuFlyoutItem(
text: Text(entry.value),
onPressed: () {
final oldValue = _dllController.customGameServer.value;
if(oldValue == entry.key) {
return;
}
_dllController.customGameServer.value = entry.key; Widget get _internalFilesServerType => SettingTile(
_dllController.infoBarEntry?.close(); icon: Icon(
if(!entry.key) { FluentIcons.games_24_regular
_dllController.updateGameServerDll(
force: true
);
}
}
)).toList()
))
), ),
Obx(() { title: Text(translations.settingsServerTypeName),
if(!_dllController.customGameServer.value) { subtitle: Text(translations.settingsServerTypeDescription),
return const SizedBox.shrink(); contentWidth: SettingTile.kDefaultContentWidth + 30,
} content: Obx(() => DropDownButton(
onOpen: () => inDialog = true,
onClose: () => inDialog = false,
leading: Text(_dllController.customGameServer.value ? translations.settingsServerTypeCustomName : translations.settingsServerTypeEmbeddedName),
items: {
false: translations.settingsServerTypeEmbeddedName,
true: translations.settingsServerTypeCustomName
}.entries.map((entry) => MenuFlyoutItem(
text: Text(entry.value),
onPressed: () {
final oldValue = _dllController.customGameServer.value;
if(oldValue == entry.key) {
return;
}
return createFileSetting( _dllController.customGameServer.value = entry.key;
title: translations.settingsServerFileName, _dllController.infoBarEntry?.close();
description: translations.settingsServerFileDescription, if(!entry.key) {
controller: _dllController.gameServerDll, _dllController.updateGameServerDll(
onReset: () { force: true
final path = _dllController.getDefaultDllPath(InjectableDll.reboot); );
_dllController.gameServerDll.text = path; }
_dllController.downloadCriticalDllInteractive(path); }
} )).toList()
); ))
}), );
Obx(() {
if(_dllController.customGameServer.value) {
return const SizedBox.shrink();
}
return SettingTile( Widget get _internalFilesServerSource => Obx(() {
icon: Icon( if(!_dllController.customGameServer.value) {
FluentIcons.globe_24_regular return SettingTile(
), icon: Icon(
title: Text(translations.settingsServerMirrorName), FluentIcons.globe_24_regular
subtitle: Text(translations.settingsServerMirrorDescription), ),
content: Row( title: Text(translations.settingsServerMirrorName),
children: [ subtitle: Text(translations.settingsServerMirrorDescription),
Expanded( contentWidth: SettingTile.kDefaultContentWidth + 30,
child: TextFormBox( content: Row(
placeholder: translations.settingsServerMirrorPlaceholder, children: [
controller: _dllController.url, Expanded(
validator: _checkUpdateUrl child: TextFormBox(
), placeholder: translations.settingsServerMirrorPlaceholder,
), controller: _dllController.url,
const SizedBox(width: 8.0), onChanged: (value) {
Button( if(Uri.tryParse(value) != null) {
style: ButtonStyle( _dllController.updateGameServerDll(force: true);
padding: ButtonState.all(EdgeInsets.zero) }
),
onPressed: () => _dllController.url.text = kRebootDownloadUrl,
child: SizedBox.square(
dimension: 30,
child: Icon(
FluentIcons.arrow_reset_24_regular
),
)
)
],
)
);
}),
Obx(() {
if(_dllController.customGameServer.value) {
return const SizedBox.shrink();
}
return SettingTile(
icon: Icon(
FluentIcons.timer_24_regular
),
title: Text(translations.settingsServerTimerName),
subtitle: Text(translations.settingsServerTimerSubtitle),
content: Row(
children: [
Expanded(
child: Obx(() => DropDownButton(
onOpen: () => inDialog = true,
onClose: () => inDialog = false,
leading: Text(_dllController.timer.value.text),
items: UpdateTimer.values.map((entry) => MenuFlyoutItem(
text: Text(entry.text),
onPressed: () {
_dllController.timer.value = entry;
_dllController.infoBarEntry?.close();
_dllController.updateGameServerDll(
force: true
);
}
)).toList()
)),
),
const SizedBox(width: 8.0),
Button(
style: ButtonStyle(
padding: ButtonState.all(EdgeInsets.zero)
),
onPressed: () {
_dllController.updateGameServerDll(force: true);
}, },
),
),
const SizedBox(width: 8.0),
Button(
style: ButtonStyle(
padding: ButtonState.all(EdgeInsets.zero)
),
onPressed: () => _dllController.updateGameServerDll(force: true),
child: SizedBox.square( child: SizedBox.square(
dimension: 30, dimension: 30,
child: Icon( child: Icon(
FluentIcons.arrow_download_24_regular FluentIcons.arrow_download_24_regular
), ),
) )
) ),
], const SizedBox(width: 8.0),
) Button(
); style: ButtonStyle(
}) padding: ButtonState.all(EdgeInsets.zero)
], ),
); onPressed: () {
_dllController.url.text = kRebootDownloadUrl;
_dllController.updateGameServerDll(force: true);
},
child: SizedBox.square(
dimension: 30,
child: Icon(
FluentIcons.arrow_reset_24_regular
),
)
)
],
)
);
}else {
return createFileSetting(
title: translations.settingsServerFileName,
description: translations.settingsServerFileDescription,
controller: _dllController.gameServerDll,
onReset: () {
final path = _dllController.getDefaultDllPath(InjectableDll.reboot);
_dllController.gameServerDll.text = path;
_dllController.downloadCriticalDllInteractive(path);
}
);
}
});
Widget get _internalFilesUpdateTimer => Obx(() {
String? _checkUpdateUrl(String? text) { if(_dllController.customGameServer.value) {
if (text == null || text.isEmpty) { return const SizedBox.shrink();
return translations.emptyURL;
} }
return null; return SettingTile(
} icon: Icon(
FluentIcons.timer_24_regular
),
title: Text(translations.settingsServerTimerName),
subtitle: Text(translations.settingsServerTimerSubtitle),
contentWidth: SettingTile.kDefaultContentWidth + 30,
content: Obx(() => DropDownButton(
onOpen: () => inDialog = true,
onClose: () => inDialog = false,
leading: Text(_dllController.timer.value.text),
items: UpdateTimer.values.map((entry) => MenuFlyoutItem(
text: Text(entry.text),
onPressed: () {
_dllController.timer.value = entry;
_dllController.infoBarEntry?.close();
_dllController.updateGameServerDll(
force: true
);
}
)).toList()
))
);
});
SettingTile get _share => SettingTile( SettingTile get _share => SettingTile(
icon: Icon( icon: Icon(

View File

@@ -110,9 +110,9 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
contentWidth: null, contentWidth: null,
content: Row( content: Row(
children: [ children: [
Text( Obx(() => Text(
_settingsController.debug.value ? translations.on : translations.off _settingsController.debug.value ? translations.on : translations.off
), )),
const SizedBox( const SizedBox(
width: 16.0 width: 16.0
), ),

View File

@@ -5,4 +5,15 @@ extension IterableExtension<E> on Iterable<E> {
} }
return null; return null;
} }
}
extension StringExtension on String {
String? after(String leading) {
final index = indexOf(leading);
if(index == -1) {
return null;
}
return substring(index + leading.length);
}
} }

View File

@@ -19,10 +19,10 @@ class FileSelector extends StatefulWidget {
required this.controller, required this.controller,
required this.validator, required this.validator,
required this.folder, required this.folder,
required this.allowNavigator,
this.label, this.label,
this.extension, this.extension,
this.validatorMode, this.validatorMode,
this.allowNavigator = true,
Key? key}) Key? key})
: assert(folder || extension != null, "Missing extension for file selector"), : assert(folder || extension != null, "Missing extension for file selector"),
super(key: key); super(key: key);

View File

@@ -1,70 +1,120 @@
import 'dart:io'; import 'dart:io';
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons; import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
import 'package:fluent_ui/fluent_ui.dart' as fluentIcons show FluentIcons;
import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:fluentui_system_icons/fluentui_system_icons.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/util/translations.dart'; import 'package:reboot_launcher/src/util/translations.dart';
import 'package:reboot_launcher/src/widget/file_selector.dart'; import 'package:reboot_launcher/src/widget/file_selector.dart';
import 'package:reboot_launcher/src/widget/setting_tile.dart'; import 'package:reboot_launcher/src/widget/setting_tile.dart';
const double _kButtonDimensions = 30;
const double _kButtonSpacing = 8;
SettingTile createFileSetting({required String title, required String description, required TextEditingController controller, required void Function() onReset}) { SettingTile createFileSetting({required String title, required String description, required TextEditingController controller, required void Function() onReset}) {
final obx = RxString(controller.text); final obx = RxString(controller.text);
controller.addListener(() => obx.value = controller.text); controller.addListener(() => obx.value = controller.text);
return SettingTile( final selecting = RxBool(false);
icon: Icon( return SettingTile(
FluentIcons.document_24_regular icon: Icon(
), FluentIcons.document_24_regular
title: Text(title), ),
subtitle: Text(description), title: Text(title),
content: Row( subtitle: Text(description),
contentWidth: SettingTile.kDefaultContentWidth + _kButtonDimensions * 2 + _kButtonSpacing * 2,
content: Row(
children: [ children: [
Expanded( Expanded(
child: FileSelector( child: FileSelector(
placeholder: translations.selectPathPlaceholder, placeholder: translations.selectPathPlaceholder,
windowTitle: translations.selectPathWindowTitle, windowTitle: translations.selectPathWindowTitle,
controller: controller, controller: controller,
validator: _checkDll, validator: _checkDll,
extension: "dll", extension: "dll",
folder: false, folder: false,
validatorMode: AutovalidateMode.always validatorMode: AutovalidateMode.always,
), allowNavigator: false,
), ),
const SizedBox(width: 8.0), ),
Obx(() => Padding( const SizedBox(width: _kButtonSpacing),
padding: EdgeInsets.only( Obx(() => Padding(
bottom: _checkDll(obx.value) == null ? 0.0 : 20.0 padding: EdgeInsets.only(
bottom: _checkDll(obx.value) == null ? 0.0 : 20.0
),
child: Tooltip(
message: translations.selectFile,
child: Button(
style: ButtonStyle(
padding: ButtonState.all(EdgeInsets.zero)
),
onPressed: () => _onPressed(selecting, controller),
child: SizedBox.square(
dimension: _kButtonDimensions,
child: Icon(
fluentIcons.FluentIcons.open_folder_horizontal
),
)
), ),
),
)),
const SizedBox(width: _kButtonSpacing),
Obx(() => Padding(
padding: EdgeInsets.only(
bottom: _checkDll(obx.value) == null ? 0.0 : 20.0
),
child: Tooltip(
message: translations.reset,
child: Button( child: Button(
style: ButtonStyle( style: ButtonStyle(
padding: ButtonState.all(EdgeInsets.zero) padding: ButtonState.all(EdgeInsets.zero)
), ),
onPressed: onReset, onPressed: onReset,
child: SizedBox.square( child: SizedBox.square(
dimension: 30, dimension: _kButtonDimensions,
child: Icon( child: Icon(
FluentIcons.arrow_reset_24_regular FluentIcons.arrow_reset_24_regular
), ),
) )
), ),
)) ),
))
], ],
) )
); );
}
void _onPressed(RxBool selecting, TextEditingController controller) {
if(selecting.value){
return;
}
selecting.value = true;
compute(openFilePicker, "dll")
.then((value) => _updateText(controller, value))
.then((_) => selecting.value = false);
}
void _updateText(TextEditingController controller, String? value) {
final text = value ?? controller.text;
controller.text = text;
controller.selection = TextSelection.collapsed(offset: text.length);
} }
String? _checkDll(String? text) { String? _checkDll(String? text) {
if (text == null || text.isEmpty) { if (text == null || text.isEmpty) {
return translations.invalidDllPath; return translations.invalidDllPath;
} }
final file = File(text); final file = File(text);
if (!file.existsSync()) { if (!file.existsSync()) {
return translations.dllDoesNotExist; return translations.dllDoesNotExist;
} }
if (!text.endsWith(".dll")) { if (!text.endsWith(".dll")) {
return translations.invalidDllExtension; return translations.invalidDllExtension;
} }
return null; return null;
} }

View File

@@ -24,19 +24,22 @@ class InfoBarAreaState extends State<InfoBarArea> {
} }
@override @override
Widget build(BuildContext context) => Obx(() => Padding( Widget build(BuildContext context) => StreamBuilder(
padding: EdgeInsets.only( stream: pagesController.stream,
bottom: hasPageButton ? 72.0 : 16.0 builder: (context, _) => Obx(() => Padding(
), padding: EdgeInsets.only(
child: Column( bottom: hasPageButton ? 72.0 : 16.0
mainAxisAlignment: MainAxisAlignment.end, ),
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: _children.value.map((child) => Padding( mainAxisAlignment: MainAxisAlignment.end,
padding: EdgeInsets.only( crossAxisAlignment: CrossAxisAlignment.start,
top: 12.0 children: _children.value.map((child) => Padding(
), padding: EdgeInsets.only(
child: child top: 12.0
)).toList(growable: false) ),
), child: child
)); )).toList(growable: false)
),
))
);
} }

View File

@@ -1,6 +1,6 @@
name: reboot_launcher name: reboot_launcher
description: Graphical User Interface for Project Reboot description: Graphical User Interface for Project Reboot
version: "9.2.6" version: "9.2.7"
publish_to: 'none' publish_to: 'none'