5

我正在使用以下包https://pub.dev/packages/get。我需要在 GetxController 的 onClose 中关闭我的 .obs 吗?我在文档中找不到任何关于此的内容。看着我的记忆,似乎它们正在被自动销毁。

4

3 回答 3

7

到目前为止,我对 GetX + Flutter 的理解...

不,您不必在 GetxControllers 的close()方法中删除 .obs。当控制器从内存中删除时,控制器中的可观察对象的处理会自动完成。

当包含它们的小部件从小部件堆栈中弹出/从小部件树中删除(默认情况下,但可以被覆盖)时,GetX 会处理/删除 GetxControllers(及其可观察对象)。

您可以在覆盖dispose()各种 Get 小部件的方法中看到这一点。

这是弹出/删除小部件dispose()时运行的片段:GetX

  @override
  void dispose() {
    if (widget.dispose != null) widget.dispose(this);
    if (isCreator || widget.assignId) {
      if (widget.autoRemove && GetInstance().isRegistered<T>(tag: widget.tag)) {
        GetInstance().delete<T>(tag: widget.tag);
      }
    }
    subs.cancel();
    _observer.close();
    controller = null;
    isCreator = null;
    super.dispose();
  }

当您使用 Bindings 或Get.to()使用GetPageRoute's 时,它会按路由名称进行清理:

  @override
  void dispose() {
    if (Get.smartManagement != SmartManagement.onlyBuilder) {
      WidgetsBinding.instance.addPostFrameCallback((_) => GetInstance()
          .removeDependencyByRoute("${settings?.name ?? routeName}"));
    }
    super.dispose();
  }

测试应用

下面是一个测试应用程序,您可以将其复制/粘贴到 Android Studio / VSCode 中并运行以观察调试或运行窗口输出以获得 GETX 生命周期事件。

GetX 将记录控制器在内存中的创建和处理。

GetX 输出日志

该应用程序有一个 HomePage 和 3 个 ChildPages,它们以 3 种方式使用 Get Controller,所有这些都从内存中删除:

  1. GetX/GetBuilder
  2. 获取.put
  3. 绑定
import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  // MyCounterBinding().dependencies(); // usually where Bindings happen
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'GetX Dispose Ex',
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('GetX Dispose Test'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            RaisedButton(
              child: Text('GetX/Builder Child'),
              onPressed: () => Get.to(ChildPage()),
            ),
            RaisedButton(
              child: Text('Get.put Child'),
              onPressed: () => Get.to(ChildPutPage()),
            ),
            RaisedButton(
              child: Text('Binding Child'),
              onPressed: () => Get.to(ChildBindPage()),
            ),
          ],
        ),
      ),
    );
  }
}

/// GETX / GETBUILDER
/// Creates Controller within the Get widgets
class ChildPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('GetX Dispose Test Counter'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            Text('This is the Child Page'),
            GetX<ChildX>(
              init: ChildX(),
              builder: (cx) => Text('Counter: ${cx.counter}', style: TextStyle(fontSize: 20),),
            ),
            GetBuilder<ChildX>(
              init: ChildX(),
              builder: (cx) => RaisedButton(
                child: Text('Increment'),
                onPressed: cx.inc,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

/// GET.PUT
/// Creates Controller instance upon Build, usable anywhere within the widget build context
class ChildPutPage extends StatelessWidget {
  //final ChildX cx = Get.put(ChildX()); // wrong place to put  
  // see https://github.com/jonataslaw/getx/issues/818#issuecomment-733652172

  @override
  Widget build(BuildContext context) {
    final ChildX cx = Get.put(ChildX());
    return Scaffold(
      appBar: AppBar(
        title: Text('GetX Dispose Test Counter'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            Text('This is the Child Page'),
            Obx(
              () => Text('Counter: ${cx.counter}', style: TextStyle(fontSize: 20),),
            ),
            RaisedButton(
              child: Text('Increment'),
              onPressed: cx.inc,
            )
          ],
        ),
      ),
    );
  }
}

class MyCounterBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => ChildX(), fenix: true);
  }
}

/// GET BINDINGS
/// Normally the MyCounterBinding().dependencies() call is done in main(),
/// making it available throughout the entire app.
/// A lazyPut Controller /w [fenix:true] will be created/removed/recreated as needed or
/// as specified by SmartManagement settings.
/// But to keep the Bindings from polluting the other examples, it's done within this
/// widget's build context (you wouldn't normally do this.)
class ChildBindPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    MyCounterBinding().dependencies(); // just for illustration/example

    return Scaffold(
      appBar: AppBar(
        title: Text('GetX Dispose Test Counter'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            Text('This is the Child Page'),
            Obx(
                  () => Text('Counter: ${ChildX.i.counter}', style: TextStyle(fontSize: 20),),
            ),
            RaisedButton(
              child: Text('Increment'),
              onPressed: ChildX.i.inc,
            )
          ],
        ),
      ),
    );
  }
}


class ChildX extends GetxController {
  static ChildX get i => Get.find();
  RxInt counter = 0.obs;

  void inc() => counter.value++;
}

笔记

Get.to 与 Navigator.push

在子小部件中使用Get.put()时,请确保您使用Get.to()的是导航到该子小部件,而不是 Flutter 的内置Navigator.push.

GetX 在使用GetPageRoute时将目标小部件包装在Get.to. 当导航离开/将小部件从堆栈中弹出时,此 Route 类将处理此路由中的控制器。如果您使用Navigator.push,则不会涉及 GetX 并且您不会获得此自动清理。

导航器.push

onPressed: () => Navigator.push(context, MaterialPageRoute(
                  builder: (context) => ChildPutPage())),

到达

onPressed: () => Get.to(ChildPutPage()),
于 2020-12-02T23:42:00.290 回答
2

Based from the code of the super implementation of onClose, by default it does nothing currently.

https://github.com/jonataslaw/getx/blob/7146b6a53c0648104e4f623385deaff055e0036a/lib/get_instance/src/lifecycle.dart#L56

And from the comments, it says:

  /// Called before [onDelete] method. [onClose] might be used to
  /// dispose resources used by the controller. Like closing events,
  /// or streams before the controller is destroyed.
  /// Or dispose objects that can potentially create some memory leaks,
  /// like TextEditingControllers, AnimationControllers.
  /// Might be useful as well to persist some data on disk.
  void onClose() {}

from that I think you need to manually close your streams in YourController::onClose() override function.

于 2020-11-04T14:17:19.147 回答
0

使用 GetWorkers 时,您似乎可以安全地使用 obs。运行此代码,您会注意到,当您单击按钮几次时,每页切换只会打印一次。

void main(){
  runApp(GetMaterialApp(home: TestWidget(),));
}

class TestWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
        child: Text('next'),
        onPressed: () => Get.to<SomeWidget>(SomeWidget()),
      ),
    );
  }
}

class SomeWidget extends StatelessWidget {
  RxBool isSubscribed = false.obs;

  SomeWidget() {
    ever(isSubscribed, (_) => print('test'));
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
        child: Text('back'),
        onPressed: () {
          isSubscribed.value = !isSubscribed.value;
          Get.back();
        },
      ),
    );
  }
}
于 2020-11-05T14:50:25.687 回答