adospace / reactorui-maui

MauiReactor is a MVU UI framework built on top of .NET MAUI
MIT License
588 stars 49 forks source link

Roadmap & stable release #14

Open rctec-at opened 1 year ago

rctec-at commented 1 year ago

Is there a official Roadmap and maybe a planed ETA for a stable release of this package?

adospace commented 12 months ago

Yes it's a stable release, please always pick latest from nuget

cris-m commented 11 months ago

I would like to know if there is a ways to use Behavior. Like to change color of svg image using IconTintColorBehavior of .NET MAUI Community Toolkit

adospace commented 11 months ago

@cris-m please open a specific ticket describing the issue in detail, thanks

liwoo commented 9 months ago

Hi @adospace Awesome Work 👏🏾 on ReactorUI. I've been playing around with it for the last few weeks, and even though I have had to do a lot of digging into source code to find exactly what I need (for example - how to tint Toolbar text with a different color from the ContentPage Title - as is the default in iOS), I've been pleasantly surprised by the sheer feature set and quality of this library!

I just wanted to find out what your thoughts are around having a globally applicable data management solution, something like SwiftData. Do you think one easily maintain state in a complex app (say 100s of pages) using OnMount and SetState?

I've used the Bloc Pattern in Flutter in the past and I find it easier to reason about complex apps. Thoughts?

adospace commented 8 months ago

Hi @liwoo, I'm glad you're considering MauiReactor for your apps.

Let me answer the state manager thing first and let's consider a large app only (for a small app with a few pages I don't think is a good idea to use a different state manager other than the one provided by the framework).

I'm not a huge expert on state managers but I made more than an experiment in .net app MauiReactor and Blazor using Fluxor and Memento. MauiReactor has its internal "state manager", which is minimal that serves only the purpose of the library. Someone could even complain that I call it state manager, anyway it works and it's easy to use. This is a possible integration of Flux for MauiReactor: https://gist.github.com/adospace/cbede42c410c642dbdbcafe9ece5e90b

Someone could say that state manager was invented for those languages that didn't have a proper DI container with the ability to inject services easily.

State managers can help to maintain discipline inside the project and have some advantages but really what is annoying is the complexity it adds to it.

So the real benefit comes IMHO when you have a large project + many (5+) developers on the team.

Instead, I can say that I officially fell in love with SwiftData (that I didn't know before you asked the question). I so liked it that I created my library ReactorData designed after SwiftData and so far I must say that it's pleasing to work with.

@all guys, I would love to have your opinions about ReactorData! @liwoo please check it out https://github.com/adospace/reactor-data

this is what could be a todo app using MauiReactor+ReactorData https://github.com/adospace/mauireactor-samples/blob/main/TodoApp/Pages/MainPage.cs

class MainPageState
{
    public IQuery<Todo> TodoItems { get; set; } = default!;
}

partial class MainPage : Component<MainPageState>
{
    [Inject]
    IModelContext _modelContext;

    protected override void OnMounted()
    {
        State.TodoItems = _modelContext.Query<Todo>(query => query.OrderBy(_ => _.Task));

        base.OnMounted();
    }

    public override VisualNode Render()
        => ContentPage(
            Grid("Auto, *, Auto", "*",
                TodoEditor(OnCreatedNewTask),

                CollectionView()
                    .ItemsSource(State.TodoItems, RenderItem)
                    .GridRow(1),

                Button("Clear List")
                    .OnClicked(OnClearList)
                    .GridRow(2)

            ));

    VisualNode RenderItem(Todo item)
        => Grid("54", "Auto, *",
            CheckBox()
                .IsChecked(item.Done)
                .OnCheckedChanged((s, args) => OnItemDoneChanged(item, args.Value)),
            Label(item.Task)
                .TextDecorations(item.Done ? TextDecorations.Strikethrough : TextDecorations.None)
                .VCenter()
                .GridColumn(1));

    static VisualNode TodoEditor(Action<Todo> created)
        => Render<string>(state =>
            Grid("*", "*,Auto",
                Entry()
                    .Text(state.Value ?? string.Empty)
                    .OnTextChanged(text => state.Set(s => text, false)),
                Button("Create")
                    .GridColumn(1)
                    .OnClicked(() =>
                    {
                        created(new Todo { Task = state.Value ?? "New Task" });
                        state.Set(s => string.Empty);
                    })
                )
            );

    void OnItemDoneChanged(Todo item, bool done)
    {
        item.Done = done;

        _modelContext.Update(item);
        _modelContext.Save();
    }

    void OnCreatedNewTask(Todo todo)
    {
        _modelContext.Add(todo);
        _modelContext.Save();
    }

    void OnClearList()
    {
        _modelContext.DeleteRange(State.TodoItems);
        _modelContext.Save();
    }
}
adospace commented 7 months ago

New feature alert! :)

I'm working to add the ability to style widgets in an easier and more centralized way.

Currently theming is provided directly from the .NET MAUI framework using styles defined either in XAML or in code.

In addition to the standard method (that, of course, remains and will be fully supported in the future), there will be another way to style widgets.

MauiReactor will provide a new class for each widget called after its name adding the "Styles" post-fix:. For example, you'll find LabelStyles, ButtonStyles, ViewStyles etc.

To change the default style of a widget you just have to modify its Default style in the relative static class.

for example:

LabelStyles.Default = _ => _.FontSize(28).FontFamily("MyFont");
ButtonStyles.Default = _=> _.Padding(5);

You'll be able to modify also the style of more generic classes, for example, to change the margin of all derived View classes you can simply set this style:

ViewStyles.Default = _=> _.Margin(2);

Furthermore, it will be possible to define specific selectors (like the classes in CSS) that will allow you to specify different style for the same widget. For example:

LabelStyles.Themes["H1"] = _=>_.FontSize(18);
LabelStyles.Themes["SubTitle"] = _=>_.FontSize(16);

and you can select the style you need in the component using the Theme property, like it's shown below:

VStack(
   Label("Title").Theme("H1),
   Label("SubTitle").Theme("SubTitle")
)

If you want to experiment with this new function please attach the "theming-feature" branch (https://github.com/adospace/reactorui-maui/tree/theming-feature).

Check it out!

juanyacovino77 commented 6 months ago

Wouldn't be possible to use the same source generator mechanism of [Prop], [Param], and [Inject] attributes but with State properties declared inside the class?

adospace commented 6 months ago

@juanyacovino77 it's something I'm looking into...

adospace commented 3 months ago

The theming feature now supports Dark/Light theme, MauiReactor responds automatically to theme change requests coming from the user or OS and applies the theme accordnly:

https://adospace.gitbook.io/mauireactor/components/theming#dark-theme-support

DarkThemeSupport

ThaDaVos commented 2 months ago

Just a question out of curiosity - but how does this differ from Comet which also aims for bringing MVU to MAUI? Or are you aming for MVUX? (See)

adospace commented 2 months ago

@ThaDaVos MauiReactor is a full MVU framework, it works mostly like ReactJS (and Flutter). Consider it like this: ReactJS creates components over the HTML DOM, when the render is called a new visual tree is created, and it is compared to the old one. New HTML nodes are created, old ones are removed, and existing ones are updated. MauiReactor creates components over the MAUI.NET tree of controls, and mostly the same happens: when the render is called, a new visual tree is called and compared to the old one. New maui.net controls are created, old ones are removed, and existing ones are updated.

Flutter works differently using Skia to render widgets but the core idea is similar. I was inspired by ReactJs anyway.

In MauiReactor you have views and models, views are rendered every time you call SetState() or Invalidate (similar to ReactJS). Models contain the state that is preserved between renders. Decoupling state and views make hot-reload work very well: I rarely need to restart the app when working.

More info here https://adospace.gitbook.io/mauireactor

Comet builds views more like c# markup (so in pure c#) and then creates direct binding with the state class that automatically implements Observable properties. It directly creates native controls (so it doesn't use virtual trees) through MAUI custom handlers. I can't say if it can be considered an MVU pure approach, but neither MauiReactor is. Comet seems inspired by SwiftUI.

What I like about Comet:

What I do not like:

I never used Comet so take what I say as is.

I even less know MVUX from Uno, so I'm not sure how it compares with MauiReactor (MauiReactor came before it).

ziaulhasanhamim commented 1 month ago

@adospace Can you give some idea about the performance of Reactor UI compared to MAUI with XAML and bindings. I really like this approach of building UI. XAML and MVVM just doesn't into today's world. As you said Reactor UI is a wrapper around MAUI so how much effect does it have on performance. It doesn't seem like it is using reflection or magic stuff. So I expect the performance to be good.

Another question, what does you recommend to use CollectionView or simple linq? Do they have differences in performance?

Also a big thanks to you for this much effort in the project. I really appreciate it.

adospace commented 1 month ago

Hi @ziaulhasanhamim, regarding performance I can say that is pretty much the same as the underlying framework (MAUI). Yes, MauiReactor doesn't use reflection so it's fully trimmable and AOT-able. I think MauiReactor is a bit faster to display pages on open because actually, it doesn't need to connect bindings on viewmodels (of course if you do not use bindings/MVVM or only compiled bindings then is the same).

Generally speaking, most of the performance issues I encountered in the past were caused by mistakes I made in code that were not related to the platform (and I guess the same happens with standard XAML applications).

Regarding the second question for "simple linq" do you mean just iterate over a collection and create items?

VScrollView(
  VStack(
    State.Items.Select(item => Label(item.Label))
 )
)

vs

CollectionView()
   .ItemsSource(State.Items, RenderItem())

RenderItem(MyItemType item) => Label(item.label);

In general use the CollectionView for performance. The Linq version is useful when you have a few items of disparate types with unique rendering needs (maybe a Settings page or something like that)

MoDao1990217 commented 1 week ago

Start using it and plan to write an application for practice.