diff --git a/lib/main/adapter/presentation/installation_bloc.dart b/lib/main/adapter/presentation/installation_bloc.dart index b4303da..a6c5701 100644 --- a/lib/main/adapter/presentation/installation_bloc.dart +++ b/lib/main/adapter/presentation/installation_bloc.dart @@ -62,6 +62,7 @@ class InstallationBloc extends Bloc { final newState = state.copyWith( gameVersion: event.gameVersion, savePath: event.savePath, + isEulaAgreed: event.isEulaAgreed, ); emit(newState); }); @@ -81,9 +82,11 @@ class _InstallationProgressValueChangedEvent extends InstallationEvent { class InstallationConfigurationUpdatedEvent extends InstallationEvent { final GameVersionViewModel? gameVersion; final String? savePath; + final bool? isEulaAgreed; InstallationConfigurationUpdatedEvent({ this.gameVersion, this.savePath, + this.isEulaAgreed, }); } diff --git a/lib/main/adapter/presentation/installation_state.dart b/lib/main/adapter/presentation/installation_state.dart index e70e6be..977b558 100644 --- a/lib/main/adapter/presentation/installation_state.dart +++ b/lib/main/adapter/presentation/installation_state.dart @@ -5,12 +5,14 @@ import 'package:minecraft_server_installer/vanilla/adapter/presentation/game_ver class InstallationState with EquatableMixin { final GameVersionViewModel? gameVersion; final String? savePath; + final bool isEulaAgreed; final ProgressViewModel downloadProgress; final bool isLocked; const InstallationState({ required this.gameVersion, required this.savePath, + required this.isEulaAgreed, required this.downloadProgress, required this.isLocked, }); @@ -19,6 +21,7 @@ class InstallationState with EquatableMixin { : this( gameVersion: null, savePath: null, + isEulaAgreed: false, downloadProgress: const ProgressViewModel.zero(), isLocked: false, ); @@ -27,6 +30,7 @@ class InstallationState with EquatableMixin { List get props => [ gameVersion, savePath, + isEulaAgreed, downloadProgress, isLocked, ]; @@ -34,12 +38,14 @@ class InstallationState with EquatableMixin { InstallationState copyWith({ GameVersionViewModel? gameVersion, String? savePath, + bool? isEulaAgreed, ProgressViewModel? downloadProgress, bool? isLocked, }) => InstallationState( gameVersion: gameVersion ?? this.gameVersion, savePath: savePath ?? this.savePath, + isEulaAgreed: isEulaAgreed ?? this.isEulaAgreed, downloadProgress: downloadProgress ?? this.downloadProgress, isLocked: isLocked ?? this.isLocked, ); @@ -48,5 +54,5 @@ class InstallationState with EquatableMixin { bool get isSavePathSelected => savePath != null && savePath!.isNotEmpty; - bool get canStartToInstall => isGameVersionSelected && isSavePathSelected && !isLocked; + bool get canStartToInstall => isGameVersionSelected && isSavePathSelected && isEulaAgreed && !isLocked; } diff --git a/lib/main/constants.dart b/lib/main/constants.dart index 143dda0..5c8610b 100644 --- a/lib/main/constants.dart +++ b/lib/main/constants.dart @@ -6,6 +6,7 @@ abstract class Constants { static const eulaFileName = 'eula.txt'; static const eulaFileContent = '#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://account.mojang.com/documents/minecraft_eula).\neula=true'; + static const eulaUrl = 'https://account.mojang.com/documents/minecraft_eula'; static final startScriptFileName = Platform.isWindows ? 'start.bat' : 'start.sh'; } diff --git a/lib/main/framework/ui/basic_configuration_tab.dart b/lib/main/framework/ui/basic_configuration_tab.dart index c24e4c9..7a8392a 100644 --- a/lib/main/framework/ui/basic_configuration_tab.dart +++ b/lib/main/framework/ui/basic_configuration_tab.dart @@ -3,35 +3,55 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; import 'package:minecraft_server_installer/main/adapter/presentation/installation_bloc.dart'; import 'package:minecraft_server_installer/main/adapter/presentation/installation_state.dart'; +import 'package:minecraft_server_installer/main/constants.dart'; import 'package:minecraft_server_installer/main/framework/ui/path_browsing_field.dart'; import 'package:minecraft_server_installer/main/framework/ui/strings.dart'; -import 'package:minecraft_server_installer/vanilla/adapter/presentation/game_version_view_model.dart'; import 'package:minecraft_server_installer/vanilla/framework/ui/game_version_dropdown.dart'; +import 'package:url_launcher/url_launcher.dart'; -class BasicConfigurationTab extends StatefulWidget { +class BasicConfigurationTab extends StatelessWidget { const BasicConfigurationTab({super.key}); - @override - State createState() => _BasicConfigurationTabState(); -} - -class _BasicConfigurationTabState extends State { - GameVersionViewModel? selectedGameVersion; - @override Widget build(BuildContext context) => Column( children: [ const GameVersionDropdown(), const Gap(16), const PathBrowsingField(), + const Gap(16), + _eulaCheckbox, const Spacer(), _bottomControl, ], ); + Widget get _eulaCheckbox => Row( + children: [ + Expanded( + child: BlocConsumer( + listener: (_, __) {}, + builder: (context, state) => CheckboxListTile( + title: const Text('${Strings.fieldEula} *'), + value: state.isEulaAgreed, + onChanged: (value) => context + .read() + .add(InstallationConfigurationUpdatedEvent(isEulaAgreed: value ?? false)), + controlAffinity: ListTileControlAffinity.leading, + contentPadding: EdgeInsets.zero, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), + )), + ), + IconButton( + onPressed: () => launchUrl(Uri.parse(Constants.eulaUrl)), + tooltip: Strings.tooltipEulaInfo, + icon: const Icon(Icons.info_outline), + ), + ], + ); + Widget get _bottomControl => BlocConsumer( listener: (_, __) {}, - builder: (_, state) => Row( + builder: (context, state) => Row( mainAxisAlignment: MainAxisAlignment.end, children: [ if (state.downloadProgress.isInProgress) @@ -39,7 +59,8 @@ class _BasicConfigurationTabState extends State { const Gap(32), ElevatedButton.icon( style: ElevatedButton.styleFrom(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))), - onPressed: context.watch().state.canStartToInstall ? _downloadServerFile : null, + onPressed: + context.watch().state.canStartToInstall ? () => _downloadServerFile(context) : null, icon: const Icon(Icons.download), label: const Padding( padding: EdgeInsets.symmetric(vertical: 12), @@ -50,7 +71,7 @@ class _BasicConfigurationTabState extends State { ), ); - void _downloadServerFile() { + void _downloadServerFile(BuildContext context) { context.read().add((InstallationStartedEvent())); } } diff --git a/lib/main/framework/ui/strings.dart b/lib/main/framework/ui/strings.dart index 340d38f..56292f6 100644 --- a/lib/main/framework/ui/strings.dart +++ b/lib/main/framework/ui/strings.dart @@ -1,7 +1,9 @@ abstract class Strings { static const fieldGameVersion = '遊戲版本'; static const fieldPath = '安裝路徑'; + static const fieldEula = '我同意 EULA 條款'; static const buttonStartToInstall = '開始安裝'; static const buttonBrowse = '瀏覽'; + static const tooltipEulaInfo = '點擊查看 EULA 條款'; static const dialogTitleSelectDirectory = '選擇安裝目錄'; } diff --git a/pubspec.lock b/pubspec.lock index 1c9a21e..f159f56 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -357,6 +357,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + url: "https://pub.dev" + source: hosted + version: "6.3.16" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" + url: "https://pub.dev" + source: hosted + version: "6.3.3" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + url: "https://pub.dev" + source: hosted + version: "3.1.4" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 80d9f6d..b9b2101 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: gap: ^3.0.1 http: ^1.4.0 path: ^1.9.1 + url_launcher: ^6.3.1 window_manager: ^0.5.0 dev_dependencies: