public CreateProjectProgressViewModel(FlutnetAppSettings appSettings, FlutnetProjectSettings projectSettings, NewProjectViewModel screen = null) : base("newprj_progress", screen)
        {
            _appSettings     = appSettings;
            _projectSettings = projectSettings;

            Title                = "New Project";
            Description          = "The project will take some time to generate.\nWait until the procedure finish.\n";
            IsDescriptionVisible = false;

            NextText     = "Finish";
            BackVisible  = false;
            IsFinishPage = true;

            OutputLines = new ObservableCollection <string>();
            // Observe any changes in the observable collection.
            // Note that the property has no public setters, so we
            // assume the collection is mutated by using the Add(),
            // Delete(), Clear() and other similar methods.
            OutputLines
            // Convert the collection to a stream of chunks,
            // so we have IObservable<IChangeSet<TKey, TValue>>
            // type also known as the DynamicData monad.
            .ToObservableChangeSet()
            // Each time the collection changes, we get
            // all updated items at once.
            .ToCollection()
            // Aggregate all the elements in the collection
            // into a multi-line string.
            .Select(lines => string.Join(Environment.NewLine, lines))
            // Then, we convert the multi-line string to the
            // property a multi-line TextBox can bind.
            .ToProperty(this, x => x.Output, out _output, scheduler: RxApp.MainThreadScheduler);

            // Create the command that calls Flutnet CLI
            CreateProject = ReactiveCommand.CreateFromTask(async ct =>
            {
                NewProjectInArg arguments = BuildCommandLineArg();

                CommandLineCallResult callResult = await CommandLineTools.Call <NewProjectOutArg>(arguments, ct, line =>
                {
                    OutputLines.Add(line);
                });

                // This is not the proper way to change property values and raise property change notifications:
                // we should return a public object and subscribe to the command observable
                // so that we can use ReactiveUI framework methods such as ToProperty, BindTo etc.
                if (callResult.Canceled)
                {
                    IsCanceled = true;
                }
                else if (callResult.Failed)
                {
                    IsFailed = true;
                }
                else
                {
                    OutArg result = callResult.CommandResult;
                    if (!result.Success)
                    {
                        IsFailed = true;
                    }
                    else
                    {
                        IsCompletedSuccessfully = true;
                    }
                }
            });

            CreateProject.IsExecuting.ToProperty(this, x => x.IsInProgress, out _isInProgress);
            CreateProject.IsExecuting.BindTo(this, x => x.IsBusy);

            BrowseProject = ReactiveCommand.Create(
                execute: () => Launcher.OpenFolder(Path.Combine(projectSettings.Location, projectSettings.SolutionName)),
                canExecute: this.WhenAnyValue(t => t.IsCompletedSuccessfully));

            // Execute the command when the View is activated
            Activator = new ViewModelActivator();
            this.WhenActivated(disposables =>
            {
                CreateProject.Execute(Unit.Default).Subscribe().DisposeWith(disposables);
            });

            this.WhenAnyValue(t => t.IsInProgress, t => !t).BindTo(this, t => t.NextEnabled);
        }
        public ConfigureProjectViewModel(FlutnetAppSettings appSettings, IScreen screen = null) : base("newprj_project", screen)
        {
            _appSettings = appSettings;

            Title = "New Project";

            Description          = "Edit project settings: you'll see a preview in the right panel.\nPressing \"Create\" will generate the project folder and all relative files.";
            IsDescriptionVisible = true;

            NextText     = "Create";
            IsFinishPage = false;

            BrowseLocation = ReactiveCommand.CreateFromTask(BrowseLocationAsync);
            BrowseLocation.Where(t => !string.IsNullOrEmpty(t)).BindTo(this, t => t.Location);

            AppSettings.Default.Preferences.Reload();
            _location = !string.IsNullOrEmpty(AppSettings.Default.Preferences.LastProjectLocation)
                ? AppSettings.Default.Preferences.LastProjectLocation
                : OperatingSystem.DefaultProjectsLocation();

            if (appSettings != null)
            {
                string projectName = AppNameToProjectName(appSettings.AppName);
                if (!string.IsNullOrEmpty(projectName))
                {
                    _projectName = projectName;
                }
            }
            else
            {
                _projectName       = DefaultProjectName;
                _solutionName      = DefaultSolutionName;
                _flutterModuleName = DefaultFlutterModuleName;
            }

            this.WhenAnyValue(t => t.ProjectName)
            .Do(t =>
            {
                this.RaisePropertyChanged(nameof(SolutionName));
                this.RaisePropertyChanged(nameof(FlutterModuleName));
            })
            .Select(t => ToSafeFilename(t, DefaultAppName))
            .ToProperty(this, t => t.OutputProjectName, out _outputProjectName, initialValue: ProjectName);

            this.WhenAnyValue(t => t.OutputProjectName)
            .Select(t => $"{t}.Android")
            .ToProperty(this, t => t.OutputProjectDroidName, out _outputProjectDroidName, initialValue: $"{OutputProjectName}.Android");

            this.WhenAnyValue(t => t.OutputProjectName)
            .Select(t => $"{t}.iOS")
            .ToProperty(this, t => t.OutputProjectIosName, out _outputProjectIosName, initialValue: $"{OutputProjectName}.iOS");

            this.WhenAnyValue(t => t.OutputProjectName)
            .Select(t => $"{t}.ServiceLibrary")
            .ToProperty(this, t => t.OutputProjectServiceLibName, out _outputProjectServiceLibName, initialValue: $"{OutputProjectName}.ServiceLibrary");

            this.WhenAnyValue(t => t.SolutionName)
            .Select(t => ToSafeFilename(t, DefaultSolutionName))
            .ToProperty(this, t => t.OutputSolutionName, out _outputSolutionName, initialValue: SolutionName ?? DefaultSolutionName);

            this.WhenAnyValue(t => t.FlutterModuleName)
            .Select(ToSafeDartPackageName)
            .ToProperty(this, t => t.OutputFlutterModuleName, out _outputFlutterModuleName, initialValue: FlutterModuleName ?? DefaultFlutterModuleName);

            this.WhenAnyValue(t => t.OutputFlutterModuleName)
            .Select(t => $"{t}_bridge")
            .ToProperty(this, t => t.OutputFlutterPackageName, out _outputFlutterPackageName, initialValue: $"{OutputFlutterModuleName}_bridge");

            this.WhenAnyValue(
                t => t.ProjectName, t => t.SolutionName, t => t.FlutterModuleName,
                (prj, sln, flutter) => !string.IsNullOrWhiteSpace(prj) && !string.IsNullOrWhiteSpace(sln) && !string.IsNullOrWhiteSpace(flutter))
            .BindTo(this, t => t.NextEnabled);

            BuildProjectTree();

            // Command to check if the installed version of Flutter is supported
            CheckFlutterVersion = ReactiveCommand.CreateFromTask(async ct =>
            {
                CommandLineCallResult callResult = await CommandLineTools.Call <FlutterInfoInArg, FlutterInfoOutArg>(ct);

                if (callResult.Canceled || callResult.Failed)
                {
                    FlutterVersionHasIssues = true;
                    FlutterVersionNotes     = "Unable to detect installed Flutter version. Compatibility with Flutnet unknown.";
                    return;
                }

                FlutterInfoOutArg result = (FlutterInfoOutArg)callResult.CommandResult;
                if (!result.Success)
                {
                    FlutterVersionHasIssues = true;
                    FlutterVersionNotes     = "Unable to detect installed Flutter version. Compatibility with Flutnet unknown.";
                    return;
                }

                StringBuilder sb = new StringBuilder();
                sb.AppendLine($"Installed Flutter version: {result.InstalledVersion}");

                switch (result.Compatibility)
                {
                case FlutterCompatibility.Supported:
                    FlutterVersionHasIssues = false;
                    break;

                case FlutterCompatibility.SupportNotGuaranteed:
                    FlutterVersionHasIssues = true;
                    sb.AppendLine("This version is NOT officially compatible with Flutnet and the resulting projects may not work or may exhibit unexpected behaviors.");
                    if (!string.IsNullOrEmpty(result.NextSupportedVersion))
                    {
                        sb.AppendLine($"We recommend that you update Flutter to the latest supported version ({result.LatestSupportedVersion}).");
                    }
                    else
                    {
                        sb.AppendLine($"The latest supported version is {result.LatestSupportedVersion}.");
                    }
                    break;

                case FlutterCompatibility.NotSupported:
                    FlutterVersionHasIssues = true;
                    NextEnabled             = false;
                    sb.AppendLine("Unfortunately this version is NOT compatible with Flutnet.");
                    sb.AppendLine($"Please update Flutter to the latest supported version ({result.LatestSupportedVersion}).");
                    break;
                }

                FlutterVersion      = result.InstalledVersion;
                FlutterVersionNotes = sb.ToString();
            });
            CheckFlutterVersion.IsExecuting.BindTo(this, x => x.IsBusy);

            this.WhenAnyValue(t => t.IsBusy).Select(t => !t).BindTo(this, p => p.NextEnabled);

            FlutterVersionNotes = "Retrieving information about the installed version of Flutter...";
        }