GetxControllers 是否自动关闭了 obs 流?
Are obs stream being closed automatically by GetxControllers?
我正在使用以下包 https://pub.dev/packages/get。我是否需要在 GetxController 的 onClose 中关闭我的 .obs?我在文档中找不到任何关于此的内容。看看我的记忆,它们似乎正在自动销毁。
基于onClose的super实现代码,目前默认什么都不做。
从评论中可以看出:
/// 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() 重写函数中手动关闭流。
看来您可以在使用 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();
},
),
);
}
}
目前为止我对GetX + Flutter的理解...
不,您不必删除 GetxControllers 的 close()
方法中的 .obs。当控制器从内存中移除时,控制器会自动处理可观察对象。
GetX disposes/removes GetxControllers(及其可观察对象)当包含它们的小部件从小部件堆栈中弹出/从小部件树中删除时(默认情况下, 但可以被覆盖)。
您可以在覆盖各种 Get 小部件的 dispose()
方法时看到这一点。
这是 dispose()
的片段 运行 当 GetX
小部件是 popped/removed:
@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();
}
当您使用绑定或 Get.to()
时,您使用的是 GetPageRoute
,它通过路由名称进行清理:
@override
void dispose() {
if (Get.smartManagement != SmartManagement.onlyBuilder) {
WidgetsBinding.instance.addPostFrameCallback((_) => GetInstance()
.removeDependencyByRoute("${settings?.name ?? routeName}"));
}
super.dispose();
}
测试应用程序
下面是一个测试应用程序,您可以 copy/paste 进入 Android Studio / VSCode 和 运行 观看调试或 运行 window GETX 生命周期事件的输出。
GetX 将在内存中和内存外记录控制器的创建和处置。
该应用程序有一个主页和 3 个子页面,使用 3 种方式获取控制器,所有这些都从内存中删除:
- GetX / GetBuilder
- Get.put
- 绑定
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
中。当导航离开/从堆栈中弹出小部件时,此路由 class 将处理此路由中的控制器。如果您使用 Navigator.push
,GetX 不参与,您将不会进行此自动清理。
Navigator.push
onPressed: () => Navigator.push(context, MaterialPageRoute(
builder: (context) => ChildPutPage())),
Get.to
onPressed: () => Get.to(ChildPutPage()),
我正在使用以下包 https://pub.dev/packages/get。我是否需要在 GetxController 的 onClose 中关闭我的 .obs?我在文档中找不到任何关于此的内容。看看我的记忆,它们似乎正在自动销毁。
基于onClose的super实现代码,目前默认什么都不做。
从评论中可以看出:
/// 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() 重写函数中手动关闭流。
看来您可以在使用 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();
},
),
);
}
}
目前为止我对GetX + Flutter的理解...
不,您不必删除 GetxControllers 的 close()
方法中的 .obs。当控制器从内存中移除时,控制器会自动处理可观察对象。
GetX disposes/removes GetxControllers(及其可观察对象)当包含它们的小部件从小部件堆栈中弹出/从小部件树中删除时(默认情况下, 但可以被覆盖)。
您可以在覆盖各种 Get 小部件的 dispose()
方法时看到这一点。
这是 dispose()
的片段 运行 当 GetX
小部件是 popped/removed:
@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();
}
当您使用绑定或 Get.to()
时,您使用的是 GetPageRoute
,它通过路由名称进行清理:
@override
void dispose() {
if (Get.smartManagement != SmartManagement.onlyBuilder) {
WidgetsBinding.instance.addPostFrameCallback((_) => GetInstance()
.removeDependencyByRoute("${settings?.name ?? routeName}"));
}
super.dispose();
}
测试应用程序
下面是一个测试应用程序,您可以 copy/paste 进入 Android Studio / VSCode 和 运行 观看调试或 运行 window GETX 生命周期事件的输出。
GetX 将在内存中和内存外记录控制器的创建和处置。
该应用程序有一个主页和 3 个子页面,使用 3 种方式获取控制器,所有这些都从内存中删除:
- GetX / GetBuilder
- Get.put
- 绑定
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
中。当导航离开/从堆栈中弹出小部件时,此路由 class 将处理此路由中的控制器。如果您使用 Navigator.push
,GetX 不参与,您将不会进行此自动清理。
Navigator.push
onPressed: () => Navigator.push(context, MaterialPageRoute(
builder: (context) => ChildPutPage())),
Get.to
onPressed: () => Get.to(ChildPutPage()),