jonataslaw / getx

Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.
MIT License
10.33k stars 1.62k forks source link

GetX 5 Migrate Guide (Draft). #2966

Open jonataslaw opened 11 months ago

jonataslaw commented 11 months ago

This is a guide that aims to provide assistance with changes and break changes that occurred in version 5. You can use this issue for immediate support if you experience any problems.

1. Bindings

We are adding some scope to the bindings. This is important, because when we use Flutter web and navigate directly through the url to the address: /products/home for example, we expect the /products bings to be loaded. In version 4 this may generate unwanted red screens. In version 5, we have a parent and child relationship between GetPages, and between Bindings.

You will probably be able to make your code start without errors (just with a deprecated warning), but if you want to update your code to the new standard, just change the "void" function to a Bind List.

Previous:

class Foo extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => Bar());
  }
}

Now:

class Foo extends Bindings {
  @override
  List<Bind> dependencies() => [
    Bind.lazyPut(() => Bar());
  ]
}

2. Immutable state manager.

Although not very well known, GetX has an immutable data manager. In version 5 it has a completely new look.

Previously we could change the immutable state and its status as follows:

 change(data, status: RxStatus.success());

Now, you don't need pass a status, rather than, you can inherit a state class:

 change(SuccessStatus(data));

Or, if you prefer, you can use the GetState classe to get all available states:

 change(GetStatus.success(data));

Or a better and clean way:

setSuccess(data)

It's important to remember, this is for if you want to manage each state individually, but GetX can take care of all of this for you through the futurize function. It allows all statuses, error, loading, and success to be done automatically, and best of all, you need just one line of code:

onInit(){
   futurize(youAsyncFunction);
}

3. Navigator 2:

GetX 5 only uses Navigator 2 under the hood, so you no longer need to use something like: Get.rootDelegate.toNamed to access navigator 2. Get.toNamed will do that. It is important to remember that now everything that applies to Flutter's Navigator 2 applies to GetX. WillPopScope no longer works, should not be used, and BackButtonListener is the new method that does the work.

If you need a custom RootDelegate, is a good idea to extend GetDelegate.

4. Get.back

Get.back is used everywhere. This was like that until GetX 4, you could close snackbars, dialogs, pages, everything. However, at the same time as this seemed magical, it had a problem: it was not possible to know what that method was closing, and that was bad. When I created this, I thought it was great, but as I matured as a programmer, I started to prefer security over anything else. From now on, Get.back() only returns one page in the Stack (as its name suggests). Get.close() no longer returns a certain number of pages in the stack (that didn't even make sense given the name), it now closes Overlays (snackbars, dialogs, bottomsheets, etc). The function name is now self-descriptive. Do you want to close something? Use Get.close(), do you want to go back to a previous page? Use Get.back(). Furthermore, in GetX we can do something incredible, which is to close only the overlay we want (I'm not sure if this can be done with Flutter Vanilla, as I had to make deep modifications to it). I worked on some apps, and I've used many apps in Flutter, which has a chronic problem, when you open an overlay on top of another, you can only close the last one or none at all. Navigator.pop(context) has no idea what it's doing, just like Get.back() did. Now you can use Get.closeSnackbar() and that's it, only your snackbar will be closed. The same goes for dialogs, bottomsheets, etc.

However, GetX 5 is much better than GetX 4 in everything. I strongly recommend you migrate. There are more than 500 bug fixes, new features, and a framework rebuilt from scratch. However, it could be challenging on large projects to redo everything from scratch, so I added some functions that maintain the previous behavior (as far as possible). Get.backLegacy() has the same behavior as Get.back() from version 4, so if you want to test version 5 now, and have no Nested routes, no StateMixin classes (where breaking changes occurred), you can simply replace o Get.back() by Get.backLegacy(). Test your app thoroughly after that, and if you have any bugs, I'll be happy to do an emergency update. It worked for me in my production applications, I hope it works for you.

5. BuildContext

Something new: The Observer(builder: (context){}) widget can be used to get the context (since Obx doesn't do it), and GetX 5 also has a SuperInheritedWidget. BuildContext is there for anyone who wants to use it, and the shortcuts (Obx, GetX) will be kept as is. GetBuilder will also be maintained, for compatibility reasons, but perhaps I will add some version of it with BuildContext.

There are many new Widgets, which are part of the refactoring. The responsibilities were better divided, merging GetBuilder with reactive programming under the hood (it doesn't change anything for you, but I eliminated a ton of code)

acquytrenthienduong commented 11 months ago

can you migrate example project to GetX version 5 when new version released?

laterdayi commented 11 months ago

There is a nice syntax in riverpod. Is it available in getx5 getbuilder?

final boredSuggestion = ref.watch(boredSuggestionProvider);
    // Perform a switch-case on the result to handle loading/error states
    return switch (boredSuggestion) {
      AsyncData(:final value) => Text('data: $value'),
      AsyncError(:final error) => Text('error: $error'),
      _ => const Text('loading'),
    };
  }
laterdayi commented 11 months ago

Now Get.close() no longer returns a certain number of pages in the stack. How should we roll back several pages in get5, A>B>C>D>E, and how to go back to B or C from E?

jonataslaw commented 11 months ago

Now Get.close() no longer returns a certain number of pages in the stack. How should we roll back several pages in get5, A>B>C>D>E, and how to go back to B or C from E?

Get.back(times:3)

Everything is still there, the difference now you should think of as version 5. If you are closing something, the name will always be "close", if you are returning, the name will always be "back".

laterdayi commented 11 months ago

Oh, I understand, closing the bottom sheet snackbar... will become close, and returning to the page will become back, which is more semantic.

jonataslaw commented 11 months ago

There is a nice syntax in riverpod. Is it available in getx5 getbuilder?

final boredSuggestion = ref.watch(boredSuggestionProvider);
    // Perform a switch-case on the result to handle loading/error states
    return switch (boredSuggestion) {
      AsyncData(:final value) => Text('data: $value'),
      AsyncError(:final error) => Text('error: $error'),
      _ => const Text('loading'),
    };
  }

In fact, this looks a lot like the StateMixin syntax, from version 3 that was released about 3 years ago. I don't know about using the dart 3 switch, but you can always with GetX observe a state and have the loading, success, error, etc. state. In fact, futurize does all this too. Look at StateMixin docs. But, FYI, here is a sample of the code.

class HomeController extends GetxController with StateMixin<String>{
  void onInit(){
     futurize(yourAsyncMethod)
  }
}

in your view

class Yourview extends GetView<HomeController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: controller.obx(
        (state)=>Text(state.name),

        // here you can put your custom loading indicator, but
        // by default would be Center(child:CircularProgressIndicator())
        onLoading: CustomLoadingIndicator(),
        onEmpty: Text('No data found'),

        // here also you can set your own error widget, but by
        // default will be an Center(child:Text(error))
        onError: (error)=>Text(error),
      ),
    );
}
laterdayi commented 11 months ago

Is it convenient to ask the specific release date of getx5? I am very much looking forward to it and using it in the project,After the release of getx5, will you watch the dynamic and fix it urgently? If so, I will refactor my current project to choose getx5

jonataslaw commented 11 months ago

Is it convenient to ask the specific release date of getx5? I am very much looking forward to it and using it in the project,After the release of getx5, will you watch the dynamic and fix it urgently? If so, I will refactor my current project to choose getx5

I recommend you test the latest release candidate. If there is nothing that completely breaks any user's code, it will be released tomorrow. I mentioned this in the other post. For my projects, everything is fine, but as each person uses GetX in a different way, I would like to know if GetX 5 will work well without so many modifications in most projects. However, I will spend the entire weekend online, so if any problems occur, I will update it immediately.

laterdayi commented 11 months ago

Okay, I'll give it a try. I saw it discussed before that getx5 will be subcontracted. Is there any documentation on this, or is there a specific name for each package?

laterdayi commented 11 months ago

Hopefully, the authors will document the migration considerations in as much detail as possible while providing the ability to split getx into multiple packages

esentis commented 11 months ago

@jonataslaw Great news of clearing out the Get.back concept. Also since we have the closeAllSnackbars method & closeCurrentSnackbar, can we get closeAllDialogs - closeCurrentDialog & closeAllModalSheets - closeCurrentModalSheeet methods ?

Will make the code much clearer alongiside with Get.isDialogOpen & Get.isBottomSheetOpen checks.

jonataslaw commented 11 months ago

Are you using nested navigation? Are you initializing the code using GetBuilder?

jonataslaw commented 11 months ago

I'm going to try to redo this animation, deactivating the cupertino animation on the previous page.

About your controller being disposed, this is strange, since inserting a middleware does practically the same thing that GetX does by default, however, there could be someone stealing the owner of the route. Is there a bottomsheet? Dialogue? Where is this happening on this route? Well, if you have time, we can do a discord call to check this out and you can share your screen, because this is very unusual.

laterdayi commented 11 months ago

@jonataslaw How is the function of getx5 split into multiple feature packs

herowws commented 11 months ago

@jonataslaw Separate GetxController and GetBuilder from GetX without needing anything else

herowws commented 11 months ago

@jonataslaw Separate GetxController and GetBuilder from GetX without needing anything else

Create a standalone plugin by separating GetxController and GetBuilder from GetX without requiring any routing, animations, GetMaterialApp, or any other dependencies

rologr35 commented 11 months ago

@jonataslaw Hello, thanks for this update. I recently update my code to v5. Now I have a situation where I need nested navigation but navigation between pages it's not working, the only page generated is the initialRoute declared in the Navigator widget. I see several changes in the code regarding this, specially that now the keys are a list of GetDelegate. The id is being saved correctly and then toNamed is exectude with the resulting delegate but debugging I realized route is null here in get_router_delegate.dart: final route = _getRouteDecoder(args); So the push is never executed. Also I would like to know if there is a way to send arguments to the initial route, I tried overriding the RouteSettings in onGenerateRoute function but I still receiving null arguments in the child. Thanks in advance

loic-hamdi commented 11 months ago

1. Bindings

We are adding some scope to the bindings. This is important, because when we use Flutter web and navigate directly through the url to the address: /products/home for example, we expect the /products bings to be loaded. In version 4 this may generate unwanted red screens. In version 5, we have a parent and child relationship between GetPages, and between Bindings.

You will probably be able to make your code start without errors (just with a deprecated warning), but if you want to update your code to the new standard, just change the "void" function to a Bind List.

Previous:

class Foo extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => Bar());
  }
}

Now:

class Foo extends Bindings {
  @override
  List<Bind> dependencies() => [
    Bind.lazyPut(() => Bar());
  ]
}

The example for the new binding is incorrect

It should be:

class Foo extends Binding {
  @override
  List<Bind> dependencies() => [Bind.lazyPut(() => Bar())];
}
fuxianwei commented 10 months ago

The latest get 5 uses update to update objects to report errors, such as:

class User { String name, last; int age; User({required this.name, required this.last, required this.age}); }

final user = User(name: 'John', last: 'Doe', age: 33).obs;

user.update((value) { value?.name='Roi'; });

Then an error message is displayed

The body might complete normally, causing 'null' to be returned, but the return type, 'User', is a potentially non-nullable type.

How can I correct the code in get 5 and fix this problem

截屏2023-11-29 14 13 08
Dabbit-Chan commented 10 months ago

As I custom the RouteDelegate, why can't I cutsom the GetNavigator in RouteDelegate? I need a custom onGenerateRoute.

bkhezry commented 10 months ago

Can you provide instructions on how to migrate the binding controller syntax in Getx 5?

Get.to(
      () => const SecondScreen(),
      binding: BindingsBuilder(() {
        Get.lazyPut(() => SecondController());
      }),
    );
loic-hamdi commented 10 months ago

Can you provide instructions on how to migrate the binding controller syntax in Getx 5?

Get.to(
      () => const SecondScreen(),
      binding: BindingsBuilder(() {
        Get.lazyPut(() => SecondController());
      }),
    );

Please see https://github.com/jonataslaw/getx/issues/2966#issuecomment-1826432699

woywie87 commented 9 months ago

@jonataslaw Hi, thank You for guide. How can I use Get.to(SecondPage()) ? In version 5, I getthis message "The argument type 'SecondPage' can't be assigned to the parameter type 'Widget Function()"

Dabbit-Chan commented 9 months ago

@jonataslaw Hi, thank You for guide. How can I use Get.to(SecondPage()) ? In version 5, I getthis message "The argument type 'SecondPage' can't be assigned to the parameter type 'Widget Function()"

Get.to(() => SecondPage())

KayesAshfaQ commented 9 months ago

Is it possible to do deep linking only with getx routing? (without any packages like unilink)

inyong1 commented 9 months ago

@jonataslaw getx 5 routing for flutter web working well in run debug, but not working after release compile

In debug mode I can enter an url in the address bar and / or refreshing the web page. But in compile release if I refresh the web page or directly enter an url in browser address bar, it entering to 404 route (not found)

Example, here the "/home" is the default route, it's open by default. image

And if I do refresh teh page it become notfound image

[EDIT] Solved. Need to configure the server to redirect all requests to index.html

laterdayi commented 7 months ago

image https://pub.dev/packages/refreshed

nugrahazzz commented 6 months ago

I used statemixin and futurize but to trigger onEmpty it doesn't work properly. For Widgets that appear in a successful position, onLoading and onError are running properly but onEmpty is not running properly even with the help of change(GetStatus.empty)... Under what conditions is onEmpty triggered? Let's say I have a List of Model, does onEmpty refer to the length of lists = 0 (.isEmpty)?

Could you please provide a simple example for onEmpty.

or change code for method futurize

void futurize(Future<T> Function() body,
      {T? initialData, String? errorMessage, bool useEmpty = true}) {
    final compute = body;
    _value ??= initialData;
    compute().then((newValue) {
      if (newValue == null && useEmpty) {
        status = GetStatus<T>.loading();
      } else {
        if( newValue!._isEmpty()){
          status = GetStatus<T>.empty();
        } else {
          status = GetStatus<T>.success(newValue);

        }
      }

      refresh();
    }, onError: (err) {
      status = GetStatus.error(errorMessage ?? err.toString());
      refresh();
    });
  }
DizzyDuan commented 6 months ago

Getx 5 swiping left to close the page conflicts with the photo_view plug-in

ettaegbe commented 5 months ago

The latest get 5 uses update to update objects to report errors, such as:

class User { String name, last; int age; User({required this.name, required this.last, required this.age}); }

final user = User(name: 'John', last: 'Doe', age: 33).obs;

user.update((value) { value?.name='Roi'; });

Then an error message is displayed

The body might complete normally, causing 'null' to be returned, but the return type, 'User', is a potentially non-nullable type.

How can I correct the code in get 5 and fix this problem 截屏2023-11-29 14 13 08

How did you fix this?

bigbott commented 3 months ago

I have migrated my (small) projects to 5.0. Here is what I have found so far: https://medium.com/@yurinovicow/flutter-getx-5-0-breaking-changes-28f381931c96

bigbott commented 3 months ago

The latest get 5 uses update to update objects to report errors, such as: class User { String name, last; int age; User({required this.name, required this.last, required this.age}); } final user = User(name: 'John', last: 'Doe', age: 33).obs; user.update((value) { value?.name='Roi'; }); Then an error message is displayed The body might complete normally, causing 'null' to be returned, but the return type, 'User', is a potentially non-nullable type. How can I correct the code in get 5 and fix this problem 截屏2023-11-29 14 13 08

How did you fix this?

https://medium.com/@yurinovicow/flutter-getx-5-0-breaking-changes-28f381931c96

adrian273 commented 3 months ago

1. Bindings

We are adding some scope to the bindings. This is important, because when we use Flutter web and navigate directly through the url to the address: /products/home for example, we expect the /products bings to be loaded. In version 4 this may generate unwanted red screens. In version 5, we have a parent and child relationship between GetPages, and between Bindings. You will probably be able to make your code start without errors (just with a deprecated warning), but if you want to update your code to the new standard, just change the "void" function to a Bind List. Previous:

class Foo extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => Bar());
  }
}

Now:

class Foo extends Bindings {
  @override
  List<Bind> dependencies() => [
    Bind.lazyPut(() => Bar());
  ]
}

The example for the new binding is incorrect

It should be:

class Foo extends Binding {
  @override
  List<Bind> dependencies() => [Bind.lazyPut(() => Bar())];
}

Thanks!

kanghk21 commented 2 months ago

Can you provide instructions on how to migrate the binding controller syntax in Getx 5?

Get.to(
      () => const SecondScreen(),
      binding: BindingsBuilder(() {
        Get.lazyPut(() => SecondController());
      }),
    );

Please see #2966 (comment)

@adrian273 I couldn't understand. Could you share the code for my code? Thanks.

     binding: BindingsBuilder(() {
                  Get.isRegistered<FirstController>()
                      ? Get.delete<FirstController>()
                      : null;
                  Get.put(SecondController());
                })),
wengxianxun commented 1 month ago

5.0版本手指拖动返回上个整个页面都可以? 会和其他手势造成冲突(iOS18)。 4.0只是在界面边缘手指拖动才可以返回。

https://github.com/user-attachments/assets/b1033a48-817f-4848-91a1-15ba49b3acaa

zyzyzhaoyong commented 1 month ago

In GetX5, the compiled web package is not in hash routing mode. How can this be handled?

if (!showHashOnUrl && GetPlatform.isWeb) setUrlStrategy(); 这是为何啊,在哪里能调用关闭啊

WMOH-DEV commented 1 month ago

5.0版本手指拖动返回上个整个页面都可以? 会和其他手势造成冲突(iOS18)。 4.0只是在界面边缘手指拖动才可以返回。

93_1726022164.mp4

I have the same issue here. If I have tabs and try to swipe from one tab to another, it goes back to the previous page (iOS behavior). Even if I don't have tabs, when I try to swipe from any part of the page, it goes back. Version 4 doesn't have this issue at all; I have to swipe from the edge, which is the correct behavior @jonataslaw

alexgrusu commented 3 weeks ago

Hey @jonataslaw ,

Thanks for the amazing work coming in with version 5.0.

I was wondering if we could introduce sending the result down the stack when we need to call until or back with times != 1

Reference: https://github.com/jonataslaw/getx/blob/cbb8c6cb4fe3e3eac47e1b8a62eb64a91f97b6be/lib/get_navigation/src/extension_navigation.dart#L820

It might not be desired to add the result to every _popWithResult call, but I believe we can find a way using the previous route (via _activePages).

example:

  void backUntil(bool Function(GetPage) predicate, {T? result}) {
    while (_activePages.length > 1 && !predicate(_activePages.last.route!)) {
      final prevPageIndex = _activePages.length - 1;

      if(predicate(_activePages[prevPageIndex].route!) {
        _popWithResult(result);
      } else {
        _popWithResult();
      }
    }

    notifyListeners();
  }
alex-aroca commented 13 hours ago

The latest get 5 uses update to update objects to report errors, such as: class User { String name, last; int age; User({required this.name, required this.last, required this.age}); } final user = User(name: 'John', last: 'Doe', age: 33).obs; user.update((value) { value?.name='Roi'; }); Then an error message is displayed The body might complete normally, causing 'null' to be returned, but the return type, 'User', is a potentially non-nullable type. How can I correct the code in get 5 and fix this problem 截屏2023-11-29 14 13 08

How did you fix this?

https://medium.com/@yurinovicow/flutter-getx-5-0-breaking-changes-28f381931c96

This don't work as well. it dont refresh the object and don't hit the obx.... If i do the oldest method model.value = newvalue; model.refresh(), shows me the error of using the refresh for the "lists only" and refresh my obs. but the new Function of update never update the view.

in the 4.6.6 the update really hit the obx because it detect a refresh of the object. but this revision don't

UPDATE: i've changed all the members of the object to obs or Rx/Rxn and now change with the new update.... It's a breaking change? If this will be true, this will be a big breaking change with a big objects observing only the main reactive object