GIfatahTH / states_rebuilder

a simple yet powerful state management technique for Flutter
494 stars 56 forks source link

This library is getting worse and worse day by day #197

Closed xoka4 closed 3 years ago

xoka4 commented 3 years ago

This library was the easiest to setup back in the days in days in v1 ( until 1.15.0 ). for an example, in v1, all you have to do is just these, to make a counter working:

void main() {
  runApp(StartingPoint());
}

class StatesContainer extends StatesRebuilder {
  int counter = 0;

  increase(int number) {
    counter += number;
    rebuildStates();
  }
}

// To make it static to access it from any classes
class StatesManager {
  static StatesContainer states = StatesContainer();
}

class StartingPoint extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StateBuilder(
      models: [StatesManager.states],
      builder: (context, _) => MaterialApp(
        home: Center(
          child: Column(
            children: [
              Text("Number: ${StatesManager.states.counter}"),
              ElevatedButton(
                onPressed: () {
                  StatesManager.states.increase(1);
                },
                child: Text(
                  "Increase",
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

But now, its just plain garbage. The devs added all those unnecessary boilerplates and breaking changes, making it harder to implement. Its really sad to see something I & my friends once loved turning into something completely horrible.

tk2232 commented 3 years ago

Not understandable to me. It's gotten a lot easier

You can write your class in plain dart

For me there is less boilerplate to rebuild widgets. Only wrap your code you want to rebuild in an On().listenTo widget

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:states_rebuilder/states_rebuilder.dart';

void main() {
  runApp(StartingPoint());
}

final counter = RM.inject<StatesContainer>(() => StatesContainer());

class StatesContainer {
  int counter = 0;

  increase(int number) {
    counter += number;
  }
}

class StartingPoint extends StatelessWidget {
  const StartingPoint({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Center(
        child: Column(
          children: [
            On(() => Text('Number: ${counter.state.counter}'))
                .listenTo(counter),
            ElevatedButton(
              onPressed: () {
                counter.setState((s) => s.increase(1));
              },
              child: Text('Increase'),
            )
          ],
        ),
      ),
    );
  }
}
GIfatahTH commented 3 years ago

@fayfay29 Sorry to read this. You are talking about v1, and now we are in version 4. Since v2, there were many breaking changes that was clearly announced at the time.

See Breaking Changes section in readme file.

I wonder why you wait until this time to complain about the breaking changed? Why not discussed at the appropriate time?

See the response of @tk2232 above and see how much cleaner the code is with less boilerplate. This is without mentioning tons of new features added since version 1.

xoka4 commented 3 years ago

I didn't write this before as I could still use the older version ( V1 ) in flutter but after upgrading to flutter 2.0, it has forced me to update the version. So, I have like 2 options:

& Why I'm saying there's more code than before. Let me show you some examples: Before ( in v1 ), the function calls are just this: states.increase(1);

but now you have to listen to the object changes and also call like this: state.setState((s) => s.increase(1));

Is this not more boilerplate/code? Which one is easier? V1 or V4? When you update something, should not you make things easier to use? How come you make things even harder to require more codes? Don't we use libraries to write less code so that the library itself would handle most of the tasks for me?

Pardon me if I sound weird. But I'm just writing my frustration. I have recommended this library to so many people. While in V1, most of the people whom I recommended could implement it within 5-10 minutes, and they really thanked me for recommending it. but after V1 ( like v2-v4 ), most people need more than 20 minutes to implement this and they kept saying, why Getx is better than this and so on, and a lot of people just stuck while reading the docs. It just feels bad to see something we loved getting worse and no-longer good as before.

amoslai5128 commented 3 years ago

state.setState((s) => s.increase(1));

I think state.setState((s) => s.increase(1)); is not related to this library at all, it's the syntax itself in Dart with Flutter and you can see that Flutter is using the same by default in StatefulWidget.

The reason to use state.setState() is it can be more flexible to deal with the state mutation from a data class, even the deep copy in the immutable object. Also, it can let you do something more during or after the setState(), for example onData, onError, onSetState, even popping up a dialog modal / snackbar whenever the state is being in certain status...

With all due respect, I recommend you to check out how to do with the immutable state in GetX (even it's not really immutable at all), you will find the answer at the link below. https://github.com/jonataslaw/getx/issues/598

There is no doubt the GetX would be a great library, and also States_rebuilder, however, nothing meaningful without a detailed Doc, the actual development must not be as simple as a counter app, the more time you spend on the greater result you will get.

GIfatahTH commented 3 years ago

& Why I'm saying there's more code than before. Let me show you some examples: Before ( in v1 ), the function calls are just this: states.increase(1); but now you have to listen to the object changes and also call like this: state.setState((s) => s.increase(1)); Is this not more boilerplate/code? Which one is easier? V1 or V4?

Notice that in your example, you are explicitly notifying listener to rebuild using rebuildStatesmethod. If you want to follow this pattern, here is your example rewritten using the new version: It's simpler than the old implementation.


void main() {
  runApp(StartingPoint());
}

class StatesContainer { //Always plain dart class
  int counter = 0;

  increase(int number) {
    counter += number;
    statesContainer.notify(); // used instead of rebuildStates()
  }
}

// Make it global to access it from any classes
final statesContainer = RM.inject(() => StatesContainer());

class StartingPoint extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StateBuilder(
      observe: () => statesContainer,
      builder: (context, _) => MaterialApp(
        home: Center(
          child: Column(
            children: [
              Text("Number: ${statesContainer.state.counter}"),
              ElevatedButton(
                onPressed: () {
                  statesContainer.state.increase(1);
                },
                child: Text(
                  "Increase",
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

BUT WAIT, the life is not that simple, and the majority of methods are asynchronous. We must handle the waiting and the error statuses.

In version 1, you deal with async call by using boolean flags and explicitly notify listeners, like this:

Your logic class becomes:

class StatesContainer extends StatesRebuilder {
  int counter = 0;
  bool isWaiting = false;
  dynamic error;
  increase(int number) async {
    try {
      isWaiting = true;
      error = null;
      rebuildStates();
      await callSomeAsyncMethod();
      isWaiting = false;
      error = null;
      rebuildStates();
    } catch (err) {
      isWaiting = false;
      error = err;
      rebuildStates();
    }
  }
}

And your UI beomes:

class StartingPoint extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StateBuilder(
      models: [StatesManager.states],
      builder: (context, _) => MaterialApp(
        home: Center(
          child: Column(
            children: [
              if (StatesManager.states.isWaiting)
                CircularProgressIndicator()
              else if (StatesManager.states.error != null)
                Text('Error')
              else
                Text("Number: ${StatesManager.states.counter}"),
              ElevatedButton(
                onPressed: () {
                  StatesManager.states.increase(1);
                },
                child: Text(
                  "Increase",
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Compare all this boilerplate to this clean and elegant code: The logic class:

class StatesContainer {
  int counter = 0;
  increase(int number) async {
    await callSomeAsyncMethod();
  }
}

The UI

class StartingPoint extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Center(
        child: Column(
          children: [
            On.all(
              onIdle: () => Text('Idle'),
              onWaiting: () => CircularProgressIndicator(),
              onError: (err, _) => Text('Error'),
              onData: () => Text("Number: ${statesContainer.state.counter}"),
            ).listenTo(statesContainer),
            ElevatedButton(
              onPressed: () {
                statesContainer.setState((s) => s.increase(1));
              },
              child: Text(
                "Increase",
              ),
            ),
          ],
        ),
      ),
    );
  }

WHAT IF you want to handle side effects that need a BuildContext such as Navigation? setState comes to rescue.

This is just one of the many features that for sure will save your development time.

xoka4 commented 3 years ago

So, usually what happens is this:

asyncFunction()
.then((data) {
    setState({
        data: data;
    })
})
.catchError((e) {

}) 

I don't understand, why you're trying to make it more complicated.

& I have never seen any developer who handles async functions like the examples you're showing ( Pardon me, cause I might not be very knowledgeable about this. But I have worked on so many people's code whether in Flutter or React ). Cause, usually we call the async functions, and in the callback/then we call the setState fuction. We don't use callbacks for setState and make decisions on changed state, do we?

I'm just saying, life doesn't have to be "Complicated". it can be "Simple", if we want it to be.

BTW, my initial intention behind writing this issue was never to say you guys are bad programmers or you guys are doing bad job. But my point was, if this library is doing so great after V1, why doesn't the statistics also say so? I mean, if in V1, 10 out of 10 people can manage to implement this, why after V1 ( V2-Vx ) most of them can't implement it and leave it halfway or just choose Getx or any other libraries?

I hope, you address that issue. & I sincerely want this library to become more popular than Getx. but not sure if it can be this way, the way it is right now.

Thanks.