【问题标题】:Are obs stream being closed automatically by GetxControllers?GetxControllers 是否会自动关闭 obs 流?
【发布时间】:2020-11-09 09:27:41
【问题描述】:

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

【问题讨论】:

  • 有人知道这个吗?
  • 我没有。还想知道。

标签: flutter dart flutter-get


【解决方案1】:

根据onClose的超级实现代码,默认当前什么都不做。

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

从 cmets 中,它说:

  /// 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() {}

我认为您需要在 YourController::onClose() 覆盖函数中手动关闭流。

【讨论】:

  • 我认为只有当您绑定流时,但如果您使用诸如 ever() 或 once() 之类的工作程序,它似乎会自动处理它。你可以调用 myObsVarialble.close()
【解决方案2】:

看来您在使用 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();
        },
      ),
    );
  }
}

【讨论】:

    【解决方案3】:

    在我目前对 GetX + Flutter 的理解中......

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

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

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

    这是dispose() 的 sn-p,在弹出/删除 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,它们通过路由名称进行清理:

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

    测试应用

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

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

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

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

    Navigator.push

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

    Get.to

    onPressed: () => Get.to(ChildPutPage()),
    

    【讨论】:

      猜你喜欢
      • 2012-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-14
      • 1970-01-01
      • 2018-03-23
      • 2013-07-28
      • 2014-04-02
      相关资源
      最近更新 更多