flutter GetX控制器未自动关闭

jei2mxaa  于 2023-05-19  发布在  Flutter
关注(0)|答案(6)|浏览(463)

我有一个在Android上运行的极简主义示例应用程序,仅使用GetX作为状态管理库。有两个屏幕LandingPage和MainScreen。从主屏面返回LandingPage屏面时,控制器未按预期自动处置。我只使用Flutter的导航,而不使用GetMaterialApp Package 。
我的期望是,当Controller被示例化时,由Controller公开的值应该被重置为其初始值。但是,Widget继续显示来自控制器的最后一个值。
我现在使用的是最新版本的Flutter和GetX:2.2.3和4.3.8
感谢你的帮助。
验证码:

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
  title: 'Flutter Demo',
  theme: ThemeData(
   
    primarySwatch: Colors.purple,
  ),
  home: LandingScreen(),
  );
 }
} 

class LandingScreen extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
  return Container(
   color: Colors.blue[800],
   child: Center(
     child: ElevatedButton(
       onPressed: () => {
         Get.to(MainScreen())
       },
       child: const Text('Navigate to Second Screen'),
     ),
    ),
  );
 }
}

class MainScreen extends StatelessWidget {
 final MyController controller = Get.put(MyController());

 @override
 Widget build(BuildContext context) {
  return Scaffold(
  body: SafeArea(
    child: Container(
      color: Colors.blueAccent,
      child: Center(
        child: Column(
          children: [
            Obx(() => Text('Clicked ${controller.count}')),
            FloatingActionButton(
              onPressed: controller.increment,
              child: Text('+'),
            ),
            ElevatedButton(
              onPressed: ()=>{Navigator.of(context).pop()},
              child: Text('Go Back'),
            )
          ],
          ),
         ),
        ),
       ),
      );
     }
    }

  class MyController extends GetxController {

   var count = 0.obs;
   void increment() => count++;

  }
n3ipq98p

n3ipq98p1#

您需要先使用GetX导航。然而,在写这个答案的时候,有一个bug导致控制器不能自动释放。因此,现在建议使用绑定或从StatefulWidget手动处理它们,直到bug被修复。

@override
  void dispose() {
    Get.delete<Controller>();
    super.dispose();
  }

11月22日更新2021您仍然可以使用上述解决方案,或者使用更优雅的方法,您可以使用Bindings沿着扩展GetView<YourController>,而不是有状态/无状态Widget。GetXController将提供StatefulWidget所需的大部分功能。您也不必使用GetX导航。

这种方法在使用嵌套导航时也有效。
要使用绑定,有多种方法解释here

c7rzv4ha

c7rzv4ha2#

自动处置

要在移除小部件时释放GetxController,在 * build()方法内部 * 使用Get.put**,而不是作为字段。这是GetX维护人员的正确用法。
然后,当MainScreen弹出时,可以丢弃控制器。

使用错误

控制器不会被释放:

class MainScreen extends StatelessWidget {
 final MyController controller = Get.put(MyController());

 @override
 Widget build(BuildContext context) {
  return Scaffold(

示例化和注册(Get.put(...)不应作为字段
否则,控制器的注册被附加到上面的小部件(即,LandingScreen),而不是MainScreen。只有在LandingScreen被释放时,MyController才会被释放。(在问题的代码中,LandingScreen是“home”Widget,只有在应用程序退出时才会对其进行处理。
为什么?
如果将控制器示例化为字段,GetX将记录该控制器的创建,其中上下文 * 当前 * 可用,小部件的上下文,* 而不是 * 示例化小部件的上下文。示例化小部件上下文直到其build()方法才可用。小部件类的示例化和build()方法的运行是两件截然不同的事情。build()方法不是widget本身的工厂构造器,而是Flutter框架 * 在 * widget示例化后使用的生命周期方法,用于膨胀widget并将其作为Element插入/挂载到渲染对象的元素树中。(这是我目前的理解)。
在问题的示例中,MainScreen小部件在LandingScreen的上下文中示例化。MainScreen及其字段位于LandingScreen的上下文中。MainScreen's build()方法在示例化后运行。MainScreen's context在其build()方法运行时可用,但在Widget示例化期间不可用。
为了在释放MainScreen时删除/释放MyController,我们必须在MainScreen上下文中示例化MyController,该上下文在调用其build(BuildContext context)方法时可用。如果我们希望GetX在当前/示例化小部件从小部件树中删除时自动处理GetxController,那么我们应该将它与该上下文相关联。

正确用法

class MainScreen extends StatelessWidget {

 @override
 Widget build(BuildContext context) {
  // ↓ instantiate/register here inside build ↓
  final MyController controller = Get.put(MyController());
  return Scaffold(

现在,当MainScreen从路由栈中弹出时,MyController将被GetX清理/释放(我假设GetX会观察路由历史以了解何时进行清理)。

bpzcxfmw

bpzcxfmw3#

你可以像下面这样延迟初始化你的控制器:

late final YourController controller;

在initState函数中:

@override
void initState() {
    super.initState();

    Get.delete<YourController>();
    controller = Get.put(YourController());
}

这会解决你的问题

68de4m5k

68de4m5k4#

如果在getMaterialApp上添加路由,则不需要在构建内部启动控制器:
添加'initialRoute'和'getPages'属性,并删除home属性:

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
  title: 'Flutter Demo',
  theme: ThemeData(
    primarySwatch: Colors.purple,
  ),
  initialRoute: '/landing',
  getPages: [
    GetPage(name: '/landing', page: () => LandingScreen()),
    GetPage(name: '/main', page: () => MainScreen()),
  ],
  );
 }
}

在LandingPage()中将Get.to()更改为Get.toNamed():

class LandingScreen extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
  return Container(
   color: Colors.blue[800],
   child: Center(
     child: ElevatedButton(
       onPressed: () => Get.toNamed('/main'),
       child: const Text('Navigate to Second Screen'),
     ),
    ),
  );
 }
}

在主屏幕上,将导航器弹出窗口更改为Get.back():

class MainScreen extends StatelessWidget {
 final MyController controller = Get.put(MyController());

 @override
 Widget build(BuildContext context) {
  return Scaffold(
  body: SafeArea(
    child: Container(
      color: Colors.blueAccent,
      child: Center(
        child: Column(
          children: [
            Obx(() => Text('Clicked ${controller.count}')),
            FloatingActionButton(
              onPressed: controller.increment,
              child: Text('+'),
            ),
            ElevatedButton(
              onPressed: ()=>Get.back(),
              child: Text('Go Back'),
            )
          ],
          ),
         ),
        ),
       ),
      );
     }
    }
chy5wohz

chy5wohz5#

当我在controller.dart文件中定义以下方法时,这对我来说有点工作:
延迟Readcontroller控制器;

void dispose(){ Get.delete(); controller = Get.put(ReadController()); }

然后在我的主文件中阅读.dart:

widget build(Build context){ final ReadController controller = Get.put(ReadController()); }

上面这段代码对我来说很好用
给予它一个尝试……
}

mefy6pfw

mefy6pfw6#

你需要使用getPages与导航getx的方式

Get.to(() => const NotificationsScreen())

路线:

GetMaterialApp(
          getPages: NavigatorService.onGenerateRoute(),

相关问题